import discord
import pickle
import os.path
import asyncio
from datetime import datetime
from discord.ext import commands
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# Connect to Google Sheets
scope = ["https://www.googleapis.com/auth/drive"]
creds = None
if os.path.exists("token.pickle"):
    with open("token.pickle", "rb") as token:
        creds = pickle.load(token)
if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        print("right place")
        flow = InstalledAppFlow.from_client_secrets_file("credentials.json", scope)
        creds = flow.run_local_server()
    # Save the credentials for the next run
    with open("token.pickle", "wb") as token:
        pickle.dump(creds, token)
service = build("docs", "v1", credentials=creds)
drive_service = build("drive", "v3", credentials=creds)

class General(commands.Cog):
    """Default Archive commands"""
    def __init__(self, bot):
        self.bot = bot

    @staticmethod
    def read_paragraph_element(paragraph):
        text_run = paragraph.get("textRun")
        if not text_run:
            return ""
        return text_run.get("content")

    @commands.command(name="list")
    async def archive_list(self, ctx):
        await ctx.send("The full list of archived documents can be found here. "
                       "Please do not share this link outside of the Scounting team.\n"
                       "https://drive.google.com/open?id=1kXSqsStCNbcBLwqNUvkKVo4qXDljQCqD")

    @commands.command(name="build")
    async def build(self, ctx):
        await ctx.send("One moment while I rebuild the database. This could take a while. Feel free to work "
                       "on some other things while I do this!")
        results = drive_service.files().list(pageSize=1000,
                                             fields="nextPageToken, files(id, name, mimeType, trashed)").execute()
        items = results.get('files', [])
        conn = self.bot.pool
        sql = "SELECT doc_link FROM rcs_archives"
        rows = await conn.fetch(sql)
        links = [row['doc_link'] for row in rows]
        for item in items:
            if item['id'] in links:
                continue
            if item['trashed'] or item['mimeType'] != "application/vnd.google-apps.document":
                continue
            if "ARCHIVE" in item['name']:
                try:
                    doc = service.documents().get(documentId=item['id']).execute()
                    doc_content = doc.get('body').get('content')
                    content = ""
                    for element in doc_content:
                        if "paragraph" in element:
                            paragraphs = element.get("paragraph").get("elements")
                            for paragraph in paragraphs:
                                content += self.read_paragraph_element(paragraph)
                    sql = ("INSERT INTO rcs_archives (doc_title, doc_link, doc_body) "
                           "VALUES ($1, $2, $3)")
                    await conn.execute(sql, item['name'], item['id'], content)
                    self.bot.logger.info(f"Added {item['name']} to database")
                except:
                    self.bot.logger.exception(f"Failed on {item['name']}")
        await ctx.send("Finally done.  Database is up to date!")

    @commands.command(name="search")
    async def search(self, ctx, *, search_str):
        if search_str == "list":
            await ctx.invoke(self.archive_list)
            return
        msg = await ctx.send("One moment while I crack the archives and search for your request...")
        conn = self.bot.pool
        sql = "SELECT doc_title, doc_link, doc_body FROM rcs_archives WHERE doc_title ~* $1 or doc_body ~* $2"
        rows = await conn.fetch(sql, search_str, search_str)
        file_list = ""
        if not rows:
            self.bot.logger.warning(f"No files found for {search_str}")
            await msg.edit(content=f"No files found with the text {search_str} in the title or body.")
        for row in rows:
            file_list += f"{row['doc_title']} <https://docs.google.com/document/d/{row['doc_link']}/edit>\n"
        if file_list != "":
            content = "**Files found:**\n" + file_list
            self.bot.logger.info(f"Reported:\n{file_list}")
            await msg.edit(content=content)
        self.bot.messages[ctx.message.id] = msg

    @commands.command(name="archive")
    @commands.has_any_role("Council", "RCS Scouts")
    async def archive(self, ctx, *, channel: str = None):
        """Archives the current channel.

        This function performs the following tasks:
        * Creates a new Google Doc (using a template)
        * Names the Doc using the channel name or the document name specified
        * Copies all messages from the channel to the Doc
        * Provides a link to the Doc
        * Waits 10 minutes, then deletes the Discord channel

        Parameters
        ----------
        channel : str, optional
            Desired document name if different than the channel name.
        """
        if not channel:
            channel = ctx.channel.name
        now = datetime.utcnow().strftime("%d %B %Y, %H:%M:%S")
        len_now = (len(now.encode('utf-16-le')) / 2) + 4
        doc_name = f"ARCHIVE - {channel.replace('-',' ').title()}"
        doc_name = doc_name.replace("   ", " - ")
        len_doc_name = len(doc_name.encode('utf-16-le')) / 2
        msg = await ctx.send(f"One moment while I create an archive named {doc_name}.")
        # The following template a public Doc and is important for proper formatting of archived channels.
        template_id = "15iTyuU5lax8dJiE1ur9MDnIMm3YCFnlfSXH3pSzJzhk"
        body = {"name": doc_name}
        new_doc = drive_service.files().copy(fileId=template_id, body=body).execute()
        doc_copy_id = new_doc.get('id')
        doc_copy_link = f"https://docs.google.com/document/d/{doc_copy_id}/edit"
        doc_body = ""
        # Add perms for those with a link to view
        body = {
            "role": "reader",
            "type": "anyone"
        }
        response = drive_service.permissions().create(fileId=doc_copy_id, body=body).execute()
        # Modify template fields and add messages
        requests = [
            {
                "replaceAllText": {
                    "containsText": {
                        "text": "{{CHANNEL_NAME}}",
                        "matchCase": "true"
                    },
                    "replaceText": doc_name
                }
            }, {
                "replaceAllText": {
                    "containsText": {
                        "text": "{{DATE}}",
                        "matchCase": "true"
                    },
                    "replaceText": f"{now} GMT"
                }
            }
        ]
        doc_body += f"{doc_name}\n{now} GMT\n\n"
        # Get message from Discord here
        start = len_doc_name + len_now + 7
        async for message in ctx.channel.history(before=ctx.message, limit=None, oldest_first=True):
            len_author = len(message.author.display_name.encode('utf-16-le')) / 2
            requests.append({
                "insertText": {
                    "location": {
                        "index": start
                    },
                    "text": message.author.display_name + "\n"
                }
            })
            doc_body += message.author.display_name + "\n"
            requests.append({
                "updateTextStyle": {
                    "range": {
                        "startIndex": start,
                        "endIndex": start + len_author
                    },
                    "textStyle": {"bold": True},
                    "fields": "bold"
                }
            })
            start += len_author + 1
            created = message.created_at.strftime("%d %B %Y, %H:%M:%S")
            len_date = len(created.encode('utf-16-le')) / 2
            requests.append({
                "insertText": {
                    "location": {
                        "index": start
                    },
                    "text": created + "\n\n"
                }
            })
            doc_body += created + "\n\n"
            start += len_date + 2
            len_message = len(message.content.encode('utf-16-le')) / 2
            requests.append({
                "insertText": {
                    "location": {
                        "index": start
                    },
                    "text": message.content + "\n\n"
                }
            })
            doc_body += message.content + "\n\n"
            start += len_message + 2
            for attachment in message.attachments:
                len_attachment = len(attachment.url.encode('utf-16-le')) / 2
                try:
                    if attachment.width > 450:
                        doc_width = 450
                    else:
                        doc_width = attachment.width
                    requests.append({
                        "insertInlineImage": {
                            "location": {
                                "index": start
                            },
                            "uri": attachment.url,
                            "objectSize": {
                                "width": {
                                    "magnitude": doc_width,
                                    "unit": "PT"
                                }
                            }
                        }
                    })
                    start += 1
                    requests.append({
                        "insertText": {
                            "location": {
                                "index": start
                            },
                            "text": attachment.url + "\n\n"
                        }
                    })
                    doc_body += attachment.url + "\n\n"
                    requests.append({
                        "updateTextStyle": {
                            "range": {
                                "startIndex": start,
                                "endIndex": start + len_attachment
                            },
                            "textStyle": {
                                "link": {
                                    "url": attachment.url
                                }
                            },
                            "fields": "link"
                        }
                    })
                    start += len_attachment + 2
                except (TypeError, AttributeError) as e:
                    # This should handle non-image attachments and effectively skip them.
                    self.bot.logger.error(f"Error on attachment. Including link only.\n{attachment.url}\n{e}")
                    requests.append({
                        "insertText": {
                            "location": {
                                "index": start
                            },
                            "text": attachment.url + "\n\n"
                        }
                    })
                    start += len_attachment + 2
                requests.append({
                    "insertText": {
                        "location": {
                            "index": start
                        },
                        "text": "--------------------\n\n"
                    }
                })
                start += 22
        result = service.documents().batchUpdate(documentId=doc_copy_id,
                                                 body={"requests": requests}).execute()
        conn = self.bot.pool
        sql = ("INSERT INTO rcs_archives (doc_title, doc_link, doc_body) "
               "VALUES ($1, $2, $3)")
        await conn.execute(sql, doc_name, doc_copy_id, doc_body)
        embed = discord.Embed(title="Archive Document Created", color=discord.Color.purple())
        embed.add_field(name="Guild", value=ctx.guild.name, inline=False)
        embed.add_field(name="Channel", value=ctx.channel.name, inline=False)
        embed.add_field(name=doc_name, value=doc_copy_link, inline=False)
        await self.bot.log_channel.send(embed=embed)
        await msg.edit(content=f"Created document with title: {doc_name}\n<{doc_copy_link}>\n"
                               f"Please check that the entire document was archived properly, then issue the "
                               f"`/delete` command.")

    @commands.command(name="delete")
    @commands.has_any_role("Council")
    async def delete_channel(self, ctx):
        """Deletes the current channel."""
        def check(r, u):
            return str(r) in reactions and u.id == ctx.author.id and r.message.id == msg.id

        msg = await ctx.send("Are you sure you want to delete this channel?")
        reactions = ["ðŸ‡¾", "ðŸ‡³"]
        for r in reactions:
            try:
                await msg.add_reaction(r)
            except:
                self.bot.logger.exception("Emoji fail")
        try:
            r, u = await self.bot.wait_for("reaction_add", check=check, timeout=60.0)
        except asyncio.TimeoutError:
            await ctx.message.delete()
            await msg.delete()
            msg = await ctx.send("Timeout - Deletion Cancelled")
            await msg.delete(delay=60.0)
            return
        if reactions.index(str(r)) == 1:
            await ctx.message.delete()
            await msg.delete()
            msg = await ctx.send("Deletion cancelled by user")
            await msg.delete(delay=15.0)
            return
        if reactions.index(str(r)) == 0:
            self.bot.logger.info(f"{ctx.channel} on {ctx.guild} deleted by {ctx.author}")
            await ctx.channel.delete(reason="Delete command from Archive Bot")

async def send_text(channel, text, block=None):
    """ Sends text to channel, splitting if necessary """
    if len(text) < 2000:
        if block:
            await channel.send(f"```{text}```")
        else:
            await channel.send(text)
    else:
        coll = ""
        for line in text.splitlines(keepends=True):
            if len(coll) + len(line) > 1994:
                # if collecting is going to be too long, send  what you have so far
                if block:
                    await channel.send(f"```{coll}```")
                else:
                    await channel.send(coll)
                coll = ""
            coll += line
        await channel.send(coll)

def setup(bot):
    bot.add_cog(General(bot))


import os
import sys
import numpy as np
import matplotlib.pyplot as plt

def plot_solution (data):
    plt.grid()
    plt.plot(data[:,0],data[:,1],label="aprox",c="blue",linestyle='--')
    plt.plot(data[:,0],data[:,3],label="analit",c="red")
    plt.xlabel("t",fontsize=15)
    plt.ylabel("y",fontsize=15)
    plt.title("Analitical x Aproximation",fontsize=14)
    plt.legend(loc=0,fontsize=14)
    plt.show()

def main():

    if (len(sys.argv) != 2):
        print("==========================================================")
        print("Usage:> %s <input_file>" % (sys.argv[0]))
        print("==========================================================")
        sys.exit(1)
    else:
        input_file = sys.argv[1]

        data = np.genfromtxt(input_file)

        plot_solution(data)

if __name__ == "__main__":
    main()


"""
Script to create calculate impact for given population exposure
Author: Sarah Hülsen
"""

from pathlib import Path

from climada.entity import Exposures
from climada.hazard import Hazard
from modules.CatImpactFuncTC import cat_impf_set
from modules.CatImpactTC import imp_per_cat

basin = 'AP'    # TC basin (e.g. AP, IO, SH, WP)
year = '2020'   # population exposure year (e.g. 2000 or 2020)
scenario = 'hist_STORM'  # current climate or SSP585 TC hazard (hist_STORM or clim_STORM_{model})

# Data paths
input_path = Path('../results/intermediate/')
output_path = Path('../results/final/')
haz_fn = f'TC_{basin}_0300as_STORM.hdf5'
pop_fn = f'exp_wp_{basin}_{year}.hdf5'

# Load hazard and exposure
haz = Hazard.from_hdf5(f'{input_path}{haz_fn}')
haz.check()
exp_pop = Exposures.from_hdf5(f'{input_path}{pop_fn}')
exp_pop.check()

# Define impact functions
impf_set = cat_impf_set('TC')

# Calculate impact
imp_cat_all = imp_per_cat(exp=exp_pop, impf_set=impf_set, haz=haz, path=output_path)
imp_cat_all.to_csv(f'{output_path}{basin}_{scenario}_STORM_{year}_wp_all_imp.csv')


from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QMessageBox
from PyQt5.QtWidgets import QMainWindow, QPushButton, QToolTip, QLCDNumber
from PyQt5.QtWidgets import QDesktopWidget, QAction, QLineEdit, QCheckBox
from PyQt5.QtWidgets import QProgressBar, QComboBox, QFontDialog, QStyleFactory
from PyQt5.QtGui import QIcon, QPixmap, QFont
from PyQt5.QtCore import QCoreApplication, pyqtSlot, QTime, QTimer, Qt
import matplotlib.pyplot as plt
import sys

class Window(QMainWindow):
    def __init__(self, title, startPoint, width, height):
        super(Window, self).__init__()
        QToolTip.setFont(QFont("Decorative", 10, QFont.Bold))
        self.title = title
        self.left, self.top = startPoint
        self.width = width
        self.height = height

    def initUI(self, maxWidth, maxHeight):
        self.setWindowTitle("simple app")
        self.setToolTip("main window")
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setMinimumHeight(self.height)
        self.setMaximumHeight(maxHeight)
        self.setMinimumWidth(self.width)
        self.setMaximumWidth(maxWidth)

    def initAppIcon(self, iconPath):
        appIcon = QIcon(iconPath)
        self.setWindowIcon(appIcon)

    def initMainMenu(self):
        self.statusBar()
        self.mainMenu = self.menuBar()
        self.newMenu = self.mainMenu.addMenu("new")
        self.openMenu = self.mainMenu.addMenu("open")
        self.shareMenu = self.mainMenu.addMenu("share")
        # ---------------------------------------------
        exitBtn = QAction("Exit", self)
        exitBtn.setShortcut("ctrl+Q")
        exitBtn.triggered.connect(self.close)
        self.shareMenu.addAction(exitBtn)

    def initToolBar(self):
        self.toolBar = self.addToolBar("funcy-tools")
        self.func1 = QAction(QIcon("gallery/pyLogo.png"), "func1", self)
        self.func1.triggered.connect(self.close)
        self.toolBar.addAction(self.func1)

    def initIcon(self, iconPath, iconPos):
        icon = QIcon(iconPath)
        label = QLabel("label1", self)
        label.move(*iconPos)
        # icon modes {Active, Disabled, Selected}
        pixMap = icon.pixmap(50, QIcon.Active, QIcon.On)
        label.setPixmap(pixMap)
        label.setToolTip("Python 3 Logo")

    def shutdown(self, buttonPos):
        btn = QPushButton("Quit", self)
        btn.move(*buttonPos)
        btn.clicked.connect(self.quitAssert)
        btn.setToolTip("exit the application")

    def about(self, buttonPos):
        about = QPushButton("about", self)
        about.move(*buttonPos)
        about.setToolTip("about the program")
        about.clicked.connect(self.aboutInfo)

    def aboutInfo(self):
        info = QMessageBox.about(self, "About Me", "Developer: Loay Wael\n test application")

    def textField(self, boxPos):
        self.txtBox = QLineEdit(self)
        self.txtBox.move(*boxPos)
        self.txtBox.resize(300, 25)

    def quitAssert(self):
        choice = QMessageBox.question(self, "Shutdown Confirmation",
                                      "do you want to shutdown the application?",
                                      QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            QCoreApplication.instance().quit()
        else:
            pass

    def centerWindow(self):
        qRect = self.frameGeometry()
        centerPoint = QDesktopWidget().availableGeometry().center()
        qRect.moveCenter(centerPoint)
        self.move(qRect.topLeft())

    def refresh(self, buttonPos):
        btn = QPushButton("Refresh", self)
        btn.move(*buttonPos)
        btn.resize(300, 25)
        btn.clicked.connect(self.onClick)
        btn.setToolTip("refresh the application")

    def initComboBox(self, comPos):
        self.comBox = QComboBox(self)
        self.comBox.addItem("windows-xp")
        self.comBox.addItem("windows-Vista")
        self.comBox.addItem("windows-7")
        self.comBox.move(*comPos)
        self.comBox.activated[str].connect(self.styleChoice)

    @pyqtSlot()
    def onClick(self):
        print("refresh-button is clicked")
        txtVal = self.txtBox.text()
        print("you typed: ", txtVal)
        self.txtBox.setText("")

    def initCheckBox(self, chkPos):
        self.chkBox = QCheckBox("Enlarege-Window", self)
        self.chkBox.resize(200, 50)
        self.chkBox.move(*chkPos)
        # self.chkBox.toggle()
        self.chkBox.stateChanged.connect(self.enlargeWindow)

    def enlargeWindow(self, state):
        if state == Qt.Checked:
            self.setMaximumSize(800, 600)
            self.setGeometry(100, 100, 800, 600)
        else:
            self.setMaximumSize(400, 300)

    def initProgBar(self):
        self.downBtn = QPushButton("Download", self)
        self.downBtn.move(300, 200)
        self.downBtn.clicked.connect(self.getTicks)
        self.progBar = QProgressBar(self)
        self.progBar.setGeometry(100, 150, 300, 20)

    def changeFont(self):
        self.chgFontBtn = QPushButton("change font", self)
        self.chgFontBtn.move(100, 200)
        self.screen = QLabel("None to view", self)
        self.screen.resize(300, 30)
        self.screen.move(200, 250)
        self.chgFontBtn.clicked.connect(self.fontPicker)

    def fontPicker(self):
        font, valid = QFontDialog.getFont()
        if valid:
            self.screen.setText("empty data")
            self.screen.setFont(font)

    def getTicks(self):
        comp = 0
        while comp < 100:
            comp += 0.00001
            self.progBar.setValue(comp)

    def styleChoice(self, text):
        self.screen.setText(text)
        QApplication.setStyle(QStyleFactory.create(text))

myApp = QApplication(sys.argv)
window = Window("new window", (0, 0), 500, 500)
window.initUI(500, 500)
window.centerWindow()
window.initMainMenu()
window.about((100, 300))
window.initToolBar()
window.initAppIcon("gallery/pyLogo.png")
window.initIcon("gallery/pyLogo.png", (50, 425))
window.shutdown((400, 450))
window.initProgBar()
window.initComboBox((300, 300))
window.refresh((100, 50))
window.textField((100, 100))
window.initCheckBox((150, 350))
window.changeFont()

window.show()
# img = plt.imread("gallery/pyLogo.png")
# plt.imshow(img)
# plt.show()
myApp.exec_()
sys.exit(0)


from django_countries.fields import CountryField as ModelCountryField
from django_countries.serializer_fields import CountryField
from django_countries.serializers import CountryFieldMixin
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
from . import models
from django_countries.fields import Country
from users.serializers import UserSerializer
User = get_user_model()

class DistributorSerializer(serializers.HyperlinkedModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Distributor
        fields = ('id', 'url', 'title', 'phone', 'email', 'description', 'owner',
                  'created', 'updated', )

        read_only_fields = ('id', 'url', 'created',
                            'updated', 'owner',

                            )

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

class BodySystemSerializer(serializers.HyperlinkedModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.BodySystem
        fields = ('id', 'url', 'title', 'description', 'owner',
                  'created', 'updated', )

        read_only_fields = ('id', 'url', 'created',
                            'updated', 'owner',

                            )

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

class InstructionSerializer(serializers.HyperlinkedModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Instruction
        fields = ('id', 'url', 'title', 'description', 'owner',
                  'created', 'updated', )

        read_only_fields = ('id', 'url', 'created',
                            'updated', 'owner',

                            )

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

class PosologySerializer(serializers.HyperlinkedModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Posology
        fields = ('id', 'url', 'title', 'description', 'owner',
                  'created', 'updated', )

        read_only_fields = ('id', 'url', 'created',
                            'updated', 'owner', )

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

class FrequencySerializer(serializers.HyperlinkedModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Frequency
        fields = ('id', 'url', 'title', 'abbreviation', 'latin', 'numerical', 'description', 'owner',
                  'created', 'updated', )

        read_only_fields = ('id', 'url', 'created',
                            'updated', 'owner', )

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

class DrugClassSerializer(serializers.ModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)
    system_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.DrugClass
        fields = ('id', 'url', 'title', 'description', 'system', 'owner',
                  'created', 'updated',  'system_details')

        read_only_fields = ('id', 'url', 'created', 'system'
                            'updated', 'owner',  'system_details')

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

    def get_system_details(self, obj):
        system = models.BodySystem.objects.get(id=obj.system.id)
        return BodySystemSerializer(system, context=self.context).data

class DrugSubClassSerializer(serializers.ModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)
    drug_class_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.DrugSubClass
        fields = ('id', 'url', 'title', 'description', 'drug_class', 'owner',
                  'created', 'updated', 'drug_class_details', )

        read_only_fields = ('id', 'url', 'created',
                            'updated', 'owner', )

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

    def get_drug_class_details(self, obj):
        drug_class = models.DrugClass.objects.get(id=obj.drug_class.id)
        return DrugClassSerializer(drug_class, context=self.context).data

class GenericSerializer(serializers.ModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)
    drug_class_details = serializers.SerializerMethodField(read_only=True)
    drug_sub_class_details = serializers.SerializerMethodField(read_only=True)

    # Implement a case sensitive check for uniqueness
    title = serializers.CharField(
        max_length=240,
        validators=[
            UniqueValidator(
                queryset=models.Generic.objects.all(), lookup='iexact'

            )]
    )

    class Meta:
        model = models.Generic
        fields = ('id', 'url', 'title', 'description', 'drug_class', 'drug_sub_class', 'drug_class_details', 'drug_sub_class_details',  'owner',
                  'created', 'updated',)

        read_only_fields = ('id', 'url', 'created',
                            'updated', 'owner', )

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

    def get_drug_class_details(self, obj):
        drug_class = models.DrugClass.objects.get(id=obj.drug_class.id)
        return DrugClassSerializer(drug_class, context=self.context).data

    def get_drug_sub_class_details(self, obj):
        if obj.drug_sub_class:
            drug_sub_class = models.DrugSubClass.objects.get(
                id=obj.drug_sub_class.id)
            return DrugSubClassSerializer(drug_sub_class, context=self.context).data

class FormulationSerializer(serializers.HyperlinkedModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)

    # Implement a case sensitive check for uniqueness
    title = serializers.CharField(
        max_length=240,
        validators=[
            UniqueValidator(
                queryset=models.Formulation.objects.all(), lookup='iexact'

            )]
    )

    class Meta:
        model = models.Formulation
        fields = ('id', 'url', 'title', 'description',  'owner',
                  'created', 'updated',)

        read_only_fields = ('id', 'url', 'created',
                            'updated', 'owner', )

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

class PreparationSerializer(serializers.ModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)
    generic_details = serializers.SerializerMethodField(read_only=True)
    formulation_details = serializers.SerializerMethodField(read_only=True)
    # Implement a case sensitive check for uniqueness
    title = serializers.CharField(
        max_length=240,
        validators=[
            UniqueValidator(
                queryset=models.Preparation.objects.all(), lookup='iexact'

            )]
    )

    class Meta:
        model = models.Preparation
        fields = (
            'id',
            'url',
            'generic',
            'title',
            'formulation',
            'unit',
            'description',

            'formulation_details', 'generic_details'
        )
        read_only_fields = ('owner',)

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

    def get_generic_details(self, obj):
        generic = models.Generic.objects.get(id=obj.generic.id)
        return GenericSerializer(generic, context=self.context).data

    def get_formulation_details(self, obj):
        formulation = models.Formulation.objects.get(id=obj.formulation.id)
        return FormulationSerializer(formulation, context=self.context).data

class CountrySerializer(serializers.Serializer):
    model = Country

class CustomCountryMixin(CountryFieldMixin):
    """Custom mixin to serialize country with name instead of country code"""

    def to_representation(self, instance):
        data = super().to_representation(instance)
        for field in instance._meta.fields:
            if field.__class__ == ModelCountryField:
                if getattr(instance, field.name).name:
                    data[field.name] = getattr(instance, field.name).name
        return data

class ManufacturerSerializer(CustomCountryMixin, serializers.HyperlinkedModelSerializer):
    # owner_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Manufacturer
        fields = ('id', 'url', 'title', 'country', 'owner',
                  )

        read_only_fields = ('id', 'url',  'owner',)

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

# DISPLAY SERIALIZERS

class DistributorDisplaySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Distributor
        fields = (
            'title', 'phone', 'email',
        )

class BodySystemDisplaySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.BodySystem
        fields = (
            'title', 'description',
        )

class PosologyDisplaySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Posology
        fields = (
            'title', 'description',
        )

class FrequencyDisplaySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Frequency
        fields = (
            'title', 'numerical', 'description'
        )

class GenericDisplaySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Generic
        fields = (
            'title', 'description'
        )

class FormulationDisplaySerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = models.Formulation
        fields = (
            'title', 'description'
        )

class PreparationDisplaySerializer(serializers.HyperlinkedModelSerializer):
    item = serializers.SerializerMethodField(read_only=True)
    formulation = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Preparation
        fields = (
            'item',
            'title',
            'formulation',
            'unit',
            'description',
        )

    def get_item(self, object):
        return GenericDisplaySerializer(object.item, context=self.context).data

    def get_formulation(self, object):
        return FormulationDisplaySerializer(object.formulation, context=self.context).data

class ManufacturerDisplaySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Manufacturer
        fields = ('id', 'url',
                  'title', 'country',
                  )

class ProductSerializer(serializers.ModelSerializer):

    # owner_details = serializers.SerializerMethodField(read_only=True)
    preparation_details = serializers.SerializerMethodField(read_only=True)
    manufacturer_details = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Product
        fields = ('id', 'url', 'title', 'preparation', 'manufacturer',
                  'description', 'owner', 'units_per_pack', 'preparation_details', 'manufacturer_details', 'active', 'created', 'updated')

        read_only_fields = ('id', 'url',
                            'owner',

                            'active',
                            'created',
                            'updated')

    # def get_owner_details(self, obj):
    #     owner = User.objects.get(id=obj.owner.id)
    #     return UserSerializer(owner, context=self.context).data

    def get_preparation_details(self, obj):
        preparation = models.Preparation.objects.get(id=obj.preparation.id)
        return PreparationSerializer(preparation, context=self.context).data

    def get_manufacturer_details(self, obj):
        manufacturer = models.Manufacturer.objects.get(id=obj.manufacturer.id)
        return ManufacturerSerializer(manufacturer, context=self.context).data


import pandas as pd
import os
import click

#input = "/groups/gerlich/members/ZsuzsannaTakacs/DataScience/PythonBookClub/Tidy_data_Homework/Localinput/"
#outdir = "/groups/gerlich/members/ZsuzsannaTakacs/DataScience/PythonBookClub/Tidy_data_Homework/Localinput/test/"
#suffix = "csv"

@click.command()
@click.option('--input', '-i', help='Path of input files')
@click.option('--outdir', '-o', help='Path of output directory')
@click.option('--suffix', '-s', help='File extension')

def tidy_data_local(input, outdir, suffix):
    for filename in os.listdir(input):
        if filename.endswith('txt'):
            with open(os.path.join(input, filename)) as f:
                df = pd.read_csv(f, sep = '\t')
                df = pd.melt(df, id_vars = "Absolute time", var_name='trace')
                df['trace'] = df['trace'].str.replace('Trace', '').astype('float')
                df.to_csv(os.path.join(outdir, f'{filename}_tidy.{suffix}'), index = False)

if __name__ == '__main__':
    tidy_data_local()


# 程序创建@李宏宇
# 创建时间 ：2023/3/10

"""
1.操作文件和目录：CSDN  https://blog.csdn.net/m0_63794226/article/details/126435297?ops_request_misc=&request_id=&biz_id=102&utm_term=python%E6%93%8D%E4%BD%9C%E6%96%87%E4%BB%B6%E5%92%8C%E7%9B%AE%E5%BD%95&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-126435297.142^v73^insert_down3,201^v4^add_ask,239^v2^insert_chatgpt&spm=1018.2226.3001.4187
2.序列化：把对象转化为可传输的二进制流数据  反序列化：把二进制流数据转化为对象
    2.1.

"""
import json
import keyword
import pickle

# f = open('E:/IO编程/1.txt', 'r', encoding = 'utf-8')

# print(f.read())

import pickle

d = dict(name = 'Bob', age = 20, score = 88)
print(pickle.dumps(d))

f = open('E:/IO编程/1.txt', 'rb')
d = pickle.load(f)
f.close()
print(d)

obj = dict(name = '小明', age = 20)
s = json.dumps(obj, ensure_ascii = True)
print(s)
s = json.dumps(obj, ensure_ascii = False)
print(s)

print(keyword.kwlist)

py基础语法/IO编程/操作文件和目录、序列化.py

操作文件和目录、序列化.py


# インストールした discord.py を読み込む
import discord
import requests
from bs4 import BeautifulSoup
from sql import crud
from dotenv import load_dotenv
import os

# 環境変数(.envファイル)読み込み
load_dotenv()

# 環境変数利用
TOKEN = os.getenv('TOKEN_DISCORD')

# 接続に必要なオブジェクトを生成
client = discord.Client()

# 起動時に動作する処理

# インスタンス化
db = crud.MySQL(os.getenv('PYTHON_HOST'),os.getenv('PYTHON_USER'),os.getenv('PYTHON_PASSWORD'),os.getenv('PYTHON_PORT'),os.getenv('PYTHON_DATABASE'))

@client.event
async def on_ready():
    # 起動したらターミナルにログイン通知が表示される
    print('ログインしました')
    conn = db.connect() # 接続確認
    db.ini_connect() # テーブルが存在しなければ作成

# メッセージ受信時に動作する処理
@client.event
async def on_message(message):
    # メッセージ送信者がBotだった場合は無視する
    if message.author.bot:
        return
    # 「/amazon_now」と発言したらamazonへ情報収集
    if message.content == '/amazon_now':
        uri = os.getenv('PYTHON_HOST') + "B07X41PNSM"
        ret = requests.get(uri)
        soup = BeautifulSoup(ret.content, "html.parser")
        get_price = soup.select(
            '#corePriceDisplay_desktop_feature_div > div.a-section.a-spacing-none.aok-align-center > span > span:nth-child(2) > span.a-price-whole')
        price = '価格表示が取得できませんでした。' if get_price == None else get_price[0].contents[0].replace(
            ",", "")

        is_add_button = soup.select('#add-to-cart-button')
        button = 'ボタンなし' if is_add_button == None else 'ボタンあり'

        await message.channel.send(price)
        await message.channel.send(button)

    if '!一覧' == message.content[:3] and len(message.content) == 3:
        rows = db.get_merchandise()
        if rows == []:
            await message.channel.send('登録商品なし')
            return
        for row in rows:
            id = str(row[0])
            asin_code = row[1]
            price = str(row[2])
            name = str(row[3])
            await message.channel.send('ID=' + id + ', 商品名= '+ name +' , 価格='+ price + ', asincode='+ asin_code)

    if '!登録' in message.content:
        tmp = message.content
        msg = tmp.split('/') #スペース区切り
        print(msg)
        try:
            msg[1], msg[2], msg[3]
            name = msg[1]
            price = msg[2]
            asin_code = msg[3]

            await message.channel.send(db.add_merchandise(asin_code, price, name))

        except IndexError:
            await message.channel.send('商品名、価格、ASINCODEを正しく入力してください。')

    if '!削除' in message.content:
        tmp = message.content
        msg = tmp.split('/')
        msg
        try:
            msg[1]
            id = int(msg[1])
            await message.channel.send(db.del_merchandise(id))

        except IndexError:
            await message.channel.send('IDを入力してください')

    if '!!初期DB作成' == message.content:
        db.ini_connect()

# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)


from typing import List, Union, Type
from numpy import random
import torch
from pytorch_lightning import LightningDataModule
from torch.utils.data import Dataset, DataLoader, Subset, WeightedRandomSampler
from partglot.datamodules.datasets.partglot_dataset import PartglotDataset
from partglot.utils.simple_utils import unpickle_data
from partglot.datamodules.data_utils import split_indices_with_unseen_target_geo_in_test
from partglot.datamodules.data_utils import part_names
import os.path as osp
import os
import numpy as np
import h5py

class PartglotDataModule(LightningDataModule):
    def __init__(
        self,
        batch_size: int,
        only_correct: bool,
        only_easy_context: bool,
        max_seq_len: int,
        only_one_part_name: bool,
        seed: int,
        split_sizes: List[float],
        balance: bool,
        data_dir: str,
        **kwargs
    ):
        super().__init__()
        self.save_hyperparameters(logger=False)

        self.data_train = self.data_val = self.data_test = None
        self.prepare_data()

    def prepare_data(self):
        (
            self.game_data,
            self.word2int,
            self.int2word,
            self.int2sn,
            self.sn2int,
            self.sorted_sn,
        ) = unpickle_data(osp.join(self.hparams.data_dir, "game_data.pkl"))
        self.h5_data = h5py.File(
            osp.join(self.hparams.data_dir, "cic_bsp.h5"), "r"
        )

    def setup(self, stage=None):
        if not self.data_train and not self.data_val and not self.data_test:
            dataset = PartglotDataset(
                self.hparams, self.game_data, self.h5_data, self.word2int
            )
            split_ids = split_indices_with_unseen_target_geo_in_test(
                self.hparams.split_sizes, dataset.geo_ids, dataset.labels, seed = 2004
            )

            for i, s in enumerate(["train", "val", "test"]):
                unq = dataset.geo_ids[split_ids[s]]
                unq = np.unique(unq.flatten())
                print(f"{s} unique shape:", len(unq))

            self.data_train = Subset(dataset, split_ids["train"])
            self.data_val = Subset(dataset, split_ids["val"])
            self.data_test = Subset(dataset, split_ids["test"])

    def _build_dataloader(self, mode: str):
        self.setup()
        ds = getattr(self, "data_" + mode)

        def seed_worker(worker_id):
            worker_seed = torch.initial_seed() % 2 ** 32
            np.random.seed(worker_seed)
            random.seed(worker_seed)

        g = torch.Generator()
        g.manual_seed(0)

        if self.hparams.balance and mode == "train":
            sampler = self.get_balance_sampler(ds)

            return DataLoader(
                ds,
                batch_size=self.hparams.batch_size,
                shuffle=False,
                num_workers=4,
                pin_memory=False,
                drop_last=True,
                sampler=sampler,
                worker_init_fn=seed_worker,
                generator=g,
            )
        else:
            return DataLoader(
                ds,
                batch_size=self.hparams.batch_size,
                shuffle=mode == "train",
                num_workers=4,
                pin_memory=False,
                drop_last=mode == "train",
                worker_init_fn=seed_worker,
                generator=g,
            )
    def train_dataloader(self):
        return self._build_dataloader("train")

    def val_dataloader(self):
        return self._build_dataloader("val")

    def test_dataloader(self):
        return self._build_dataloader("test")

    def get_balance_sampler(self, ds: Union[Type[Dataset], Subset]):
        """
        Weights of Weighted Sampler are inversely proportional to the number of each part utterances.
        """
        # TODO: check Type[Dataset]

        total_ds_len = len(ds)
        weights = np.zeros(total_ds_len)

        if isinstance(ds, Subset):
            part_ind = ds.dataset.part_indicator[ds.indices]
        else:
            part_ind = ds.part_indicator

        num_example_per_part = np.sum(part_ind, axis=0)
        # fmt = f"total: {num_example_per_part.sum()}"
        # for i in range(4):
            # fmt += f"{part_names[i]}: {num_example_per_part[i]} "
        # print(fmt)
        part_ind = np.argmax(part_ind, axis=1)

        assert len(part_ind) == total_ds_len and len(num_example_per_part) == len(
            part_names
        )

        for i in range(len(part_names)):
            indices_per_part = (part_ind == i).nonzero()
            weights[indices_per_part] = 1 / num_example_per_part[i]

        g = torch.Generator()
        g.manual_seed(0)

        return WeightedRandomSampler(list(weights), len(ds), replacement=True, generator=g)


import glob
import sys

from pathlib import Path

from format_glob import format_glob

def processPaths(name, filter=None, file_name=None):
    results = []
    meta_data = []
    # if the function is called with a list or tuple, iterate over those and join the results
    if isinstance(name, (tuple, list)):
        for n in name:
            r, m = processPaths(n, filter=filter, file_name=file_name)
            results.extend(r)
            meta_data.extend(m)
    else:
        # add the file_name pattern if there is one and if it is not already there
        if file_name is not None and Path(name).suffix != Path(file_name).suffix:
            name = Path(name) / "**" / file_name
        # if it is a glob pattern, add all matching elements
        if "{" in str(name):
            for file, data in format_glob(name):
                results.append(file)
                meta_data.append(data)
        elif "*" in str(name):
            results = glob.glob(str(name), recursive=True)
            meta_data = [{}]*len(results)
        # or add the name directly
        elif Path(name).exists():
            results = [name]
            meta_data = [{}]

        # filter results if a filter is provided
        if filter is not None:
            meta_data2 = []
            results2 = []
            for n, m in zip(results, meta_data):
                print("filter", n, filter(n))
                if filter(n):
                    meta_data2.append(m)
                    results2.append(n)
            results = results2
            meta_data = meta_data2

        # if nothing was found, try to give a meaningful error message
        if len(results) == 0:
            # get a list of all parent folders
            name = Path(name).absolute()
            hierarchy = []
            while name.parent != name:
                hierarchy.append(name)
                name = name.parent
            # iterate over the parent folders, starting from the lowest
            for path in hierarchy[::-1]:
                # check if the path exists (or is a glob pattern with matches)
                if "*" in str(path):
                    exists = len(glob.glob(str(path)))
                else:
                    exists = path.exists()
                # if it does not exist, we have found our problem
                if not exists:
                    target = f"No file/folder \"{path.name}\""
                    if "*" in str(path.name):
                        target = f"Pattern \"{path.name}\" not found"
                    source = f"in folder \"{path.parent}\""
                    if "*" in str(path.parent):
                        source = f"in any folder matching the pattern \"{path.parent}\""
                    print(f"WARNING: {target} {source}", file=sys.stderr)
                    break

    return zip(results, meta_data)

if __name__ == "__main__":
    from mock_dir import MockDir
    # to test we create an artificial folder structure
    file_structure = {
        "tmp": {
            "run-1": ["run_nodes3_name-Alice.txt", "run_nodes4_name-Bob.txt", "run_nodes7_name-Foo.txt", "run_nodes7.8_name-Bar.txt"],
            "run-2": ["run_nodes3_name-Alice.txt", "run_nodes4_name-Bob.txt", "run_nodes7_name-Foo.txt", "run_nodes7.8_name-Bar.txt"],
        }
    }
    with MockDir(file_structure):

        # or get the result as a pandas dataframe
        for results, meta_data in processPaths("tmp/**/run_nodes{n:f}_name-{name}x", file_name=".txt"):
            print(results)
            print(meta_data)


# -*- coding: utf-8 -*-
"""
Created on Sun Jun  3 10:22:52 2018

@author: sande
"""
#This part of code is used to plot the residuals and find outliers in training dataset and
#find the best threshold which gives minimum number of false positives
import numpy as np
from sklearn.decomposition import PCA
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn import decomposition
import matplotlib.pyplot as plt
import sys
sys.path.append('../')
from Data.datareader import *
plt.style.use('ggplot')

features = ['L_T1', 'L_T2', 'L_T3', 'L_T4', 'L_T5', 'L_T6', 'L_T7',
     'F_PU1', 'S_PU1', 'F_PU2', 'S_PU2', 'F_PU3', 'S_PU3', 'F_PU4', 'S_PU4',
     'F_PU5', 'S_PU5', 'F_PU6', 'S_PU6', 'F_PU7', 'S_PU7', 'F_PU8', 'S_PU8',
     'F_PU9', 'S_PU9', 'F_PU10', 'S_PU10', 'F_PU11', 'S_PU11', 'F_V2', 'S_V2',
     'P_J280', 'P_J269', 'P_J300', 'P_J256', 'P_J289', 'P_J415', 'P_J302',
     'P_J306', 'P_J307', 'P_J317', 'P_J14', 'P_J422']

df3 = read_3_df()
df3 = df3[features]

df_without_outliers = df3
scaler = StandardScaler()
normalized_training = scaler.fit_transform(df3)
spe = np.zeros((normalized_training.shape[0]))

pca = decomposition.PCA(n_components=25)
pca.fit(normalized_training)
pca_model = pca.transform(normalized_training)
eigenvectors = pca.components_
P = np.transpose(eigenvectors[:-10])
P_T = np.transpose(P)
C = np.dot(P, P_T)

# Identity Matrix with the number of columns as given by training data  43 * 43
I = np.identity(43)
y_residual = np.zeros((normalized_training.shape))
for i in range(normalized_training.shape[0]):
    # Convert row to column vector
    y = np.transpose(normalized_training[i])
    y_residual[i] = np.dot(I - C, y)

#for threshold in range(270,560,30):
for threshold in range(200,500,20):
# flagged will be set to 1 if the spe is greater than the threshold
    count = 0
    flagged = np.zeros((normalized_training.shape[0]))
    for i in range(normalized_training.shape[0]):
        spe[i] = np.square(np.sum(np.subtract(y_residual[i], normalized_training[i])))
        if(spe[i] > threshold):
            flagged[i] = 1
            count = count + 1
            #hardcoding the best threshold value to avoid creating one more redundant loop just to create new dataframe
            if (threshold == 380):
                #A dataframe which contains training data without outliers and this will be used in pca_all.py
                df_without_outliers = df_without_outliers.drop(df_without_outliers.index[i])
                #Save the df in a file TrainingDfwithoutOutliers
                df_without_outliers.to_pickle("TrainingDfwithoutOutliers")

    df3 = df3.assign(ResidualVector=spe)
    df3['ResidualVector'].plot(figsize=(15,5),label="Residuals in normal data ")
    plt.axhline(y=400, c='darkgrey', linestyle='--', linewidth=1, label="400 threshold")
    plt.xlabel('Date')
    plt.ylabel('Residuals')
    plt.savefig("normal_residuals.svg", bbox_inches='tight')
    print ("number of outliers or false positives ", count, "for threshold ", threshold)
    #print ("total ", normalized_training.shape[0])

print (df3.shape)
print (df_without_outliers.shape)


# coding='utf-8'
from pathlib import Path
import re

def remove_durchen_marker(text):
    """
    this cleans up all note markers
    :param text: plain text
    :return: cleaned text
    """

    patterns = [
        # normalize single zeros '༥༥་' --> '༥༥༠

        ["༌", "་"],  # normalize NB tsek
        ["([0-9]+?)[ཝ—-]་?([0-9]+)", " <p\g<1>-\g<2>> "],
        ["”", ""],
        ["“", ""],
        ["’", ""],
        ["®", ""],
        ["=", ""],
        ["º", ""],
        ["°", ""],
        ["\*", ""],
        ["\+", ""],
        [":", ""],
        ["-", ""],
        [";", ""],
        ["�", ""],
        ['"', ''],
        ["·",""],
        ["[#€»@\{\$«\!¢\&%\?]", ""],
        ["([①-㊿༠-༩1-9]+)", ""]
    ]

    for p in patterns:
        text = re.sub(p[0], p[1], text)

    text = re.sub("<p.+?>", "", text)
    text = re.sub("([aA-zZ]+)", "", text)
    text = re.sub("<r.*?>", "", text)
    text = text.replace("<", "")
    text = text.replace(">", "")

    return text

if __name__ == "__main__":
    pecha_id = "IEA653111"
    base_path = Path(f'./test/opfs/{pecha_id}/{pecha_id}.opf/base/I1PD95878.txt')
    base_text = base_path.read_text(encoding='utf-8')
    clean_base = remove_durchen_marker(base_text)
    base_path.write_text(clean_base, encoding='utf-8')


#!/usr/bin/env python
import os
import argparse
import numpy as np
from astropy.table import Table
from astropy.coordinates import SkyCoord
import astropy.units as u

from Ska.Shell import bash
from Chandra.Time import DateTime

task = 'dark_cal_att_gen'
SHARE = os.path.join(os.environ['SKA_SHARE'], task)
DATA = os.path.join(os.environ['SKA_DATA'], task)

def get_options():
    parser = argparse.ArgumentParser(
        description="""Make a filtered version of dark_attitudes.dat using the zodiacal brightness
                       data from the attitude.pl CGI""")
    parser.add_argument("--date",
                        help="Requested date of expected dark current calibration. Defaults to now + 2 weeks")
    opt = parser.parse_args()
    return opt

def filter_atts(date):
    """
    Run the attitude.pl Perl CGI with a date and return a filtered version to remake
    dark_attitudes.dat (since the FOT is already parsing that file, I want to return a
    file that is formatted the same way).

    :param date: Chandra.Time compatible date
    :returns: astropy table of dark attitudes filtered by zodiacal brightness limits
    """
    date = DateTime(date).date

    Z_LOW = 0
    Z_UP = 250
    zodi_annotated_html = bash(
        'perl {}/attitude.pl date="{date}" type="ATT List" z_low="{z_low}" z_up="{z_up}"'.format(
            SHARE, date=date[0:8], z_low=Z_LOW, z_up=Z_UP))
    zodi_list = Table.read(zodi_annotated_html, format='ascii.html')
    ok = (zodi_list['Zodi'] >= Z_LOW) & (zodi_list['Zodi'] <= Z_UP)
    zodi_ok_coords = SkyCoord(zodi_list[ok]['RA'], zodi_list[ok]['DEC'], unit='deg')

    all_atts = Table.read(os.path.join(DATA, 'full_dark_attitudes.dat'),
                          format='ascii.fixed_width_two_line')
    ok = []
    for att in all_atts:
        coord = SkyCoord(att['eq_ra'], att['eq_dec'], unit='deg')
        ok.append(np.any(coord.separation(zodi_ok_coords) < (1 * u.arcsec)))
    ok = np.array(ok)
    cut_list = all_atts[ok]
    return cut_list

if __name__ == '__main__':
    opt = get_options()
    if opt.date is not None:
        date = DateTime(opt.date)
    else:
        date = DateTime() + 14
    cut_list = filter_atts(date)
    print("Writing out dark_attitudes.dat filtered for {}".format(date.date))
    cut_list.write(os.path.join(DATA, "dark_attitudes.dat"),
                   format='ascii.fixed_width_two_line')


import os
import pathlib
import string

async def check_cancel(message):
    """
    Проверка ввода комментария пользователем.
    @param message: сообщение пользователя.
    @return: флаг.
    """
    if message.text == '-':
        return True
    else:
        return None

def clear_whitespace(value):
    """
    Получение строки без пробелов.
    @param value: строка.
    @return: строка без пробелов.
    """
    return value.translate({ord(c): None for c in string.whitespace})

async def check_double(message, state, value):
    """
    Валидация сообщения с десятичным числом.
    @param message: объект для отправки сообщения.
    @param state: состояние бота.
    @param value: сообщение пользователя.
    @return: флаг.
    """
    try:
        if float(value) < 0:
            await message.answer("Некорректный ввод, повторите ввод:")
            return None
        return float(value)
    except:
        await message.answer("Некорректный ввод, повторите ввод:")
        await state.set()
        return None

async def check_int(message, state, value):
    """
        Валидация сообщения с целым числом.
        @param message: объект для отправки сообщения.
        @param state: состояние бота.
        @param value: сообщение пользователя.
        @return: флаг.
    """
    try:
        if int(value) < 0:
            await message.answer("Некорректный ввод, повторите ввод:")
            return None
        return int(value)
    except:
        await message.answer("Некорректный ввод, повторите ввод:")
        await state.set()
        return None

def check_folders(main_dir):
    """
    проверка на наличие папок прокта.
    @param main_dir: главная дирректория проекта.
    """
    if not os.path.exists(str(pathlib.Path(main_dir, 'images'))):
        os.mkdir(pathlib.Path(main_dir, 'images'))
        os.mkdir(str(pathlib.Path(main_dir, 'images', 'logo')))
        os.mkdir(str(pathlib.Path(main_dir, 'images', 'products_photo')))


#!/usr/bin/env python
import rospy
from sensor_msgs.msg import Image
import cv2
from cv_bridge import CvBridge
import numpy as np
from numpy.linalg import *
import signal

# SOURCE: LAB 4, IMAGE_PROCESS.PY
# Create a CvBridge to convert ROS messages to OpenCV images
bridge = CvBridge()

NUM_CUPS = 3
EPSILON = 5
BALL = -1

centroids = []

# Converts a ROS Image message to a NumPy array to be displayed by OpenCV
def ros_to_np_img(ros_img_msg):
  return np.array(bridge.imgmsg_to_cv2(ros_img_msg,'bgr8'))

def np_img_to_ros(cv2_array):
	return bridge.cv2_to_imgmsg(cv2_array, encoding='passthrough')

def do_kmeans(data, n_clusters):
    """Uses opencv to perform k-means clustering on the data given. Clusters it into
       n_clusters clusters.

       Args:
         data: ndarray of shape (n_datapoints, dim)
         n_clusters: int, number of clusters to divide into.

       Returns:
         clusters: integer array of length n_datapoints. clusters[i] is
         a number in range(n_clusters) specifying which cluster data[i]
         was assigned to.
    """
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
    _, clusters, centers = kmeans = cv2.kmeans(data.astype(np.float32), n_clusters, bestLabels=None, criteria=criteria, attempts=1, flags=cv2.KMEANS_RANDOM_CENTERS)

    return clusters

def euclidian(p1, p2):
	return np.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)

def callback(img):
	# Guassian Blur
	img_array = ros_to_np_img(img)
	img_d = cv2.resize(img_array, dsize=(int(img_array.shape[1]/4), int(img_array.shape[0]/4)), interpolation=cv2.INTER_NEAREST)
	img_blur = cv2.GaussianBlur(img_d, (21,21), 0)

	# Select out RED items
	img_mask = cv2.inRange(img_blur, np.array([0, 0, 70]), np.array([63, 63, 150]))
	img_u = cv2.resize(src=img_mask, dsize=(img_array.shape[1], img_array.shape[0]), interpolation=cv2.INTER_NEAREST)
	img_message = np_img_to_ros(img_u.astype(np.uint8))

	# publish mask
	try:
		pub_mask.publish(img_message)
	except rospy.ROSInterruptException: pass

	# Find centers of mass
	# https://learnopencv.com/find-center-of-blob-centroid-using-opencv-cpp-python/
	contours, hierarchy = cv2.findContours(img_mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
	new_centroids = []

	for c in contours:
		M = cv2.moments(c)
		if M["m00"] != 0:
			cX = int(M["m10"] / M["m00"])
			cY = int(M["m01"] / M["m00"])

			# Rule out centroids which are too close to others
			far = True
			for centroid in new_centroids:
				if euclidian([cX, cY], centroid) < EPSILON:
					far = False

			if far:
				new_centroids.append([cX, cY])

			# put text and highlight the center
			cv2.circle(img_mask, (cX, cY), 5, (0, 0, 0), -1)

	global centroids
	if len(centroids) == 0:
		new_centroids.sort(key = lambda c: c[0])
		centroids = new_centroids
		print("First iteration: " + str(centroids))
		return

	a_img_u = cv2.resize(src=img_mask, dsize=(img_array.shape[1], img_array.shape[0]), interpolation=cv2.INTER_NEAREST)
	a_img_message = np_img_to_ros(a_img_u.astype(np.uint8))

	# publish centroids
	try:
		pub_annotated.publish(a_img_message)
	except rospy.ROSInterruptException: pass

	dist = np.full((len(new_centroids), NUM_CUPS), float('inf'))

	for i in range(len(new_centroids)):
		for j in range(NUM_CUPS):
			dist[i][j] = euclidian(new_centroids[i], centroids[j])

	for n in range(NUM_CUPS):
		min_index = np.argmin(dist)
		min_i, min_j = min_index // NUM_CUPS, min_index % NUM_CUPS
		centroids[min_j] = new_centroids[min_i]
		dist[:, min_j] = np.full((1, len(new_centroids)), float('inf'))
		dist[min_i, :] = np.full(NUM_CUPS, float('inf'))

	assert len(centroids) == NUM_CUPS
	assert max(max(centroids)) < float('inf')

	print(centroids)

def observe():
	rospy.Subscriber("/usb_cam/image_raw", Image, callback)
	rospy.spin()

def handler(signum, frame):
	x_centroids = [c[0] for c in centroids]
	order = np.argsort(x_centroids)
	ball_pos = np.where(order == BALL)[0][0]
	print("The ball is at position " + str(ball_pos) + " from the left.")
	print("The ball is at " + str(centroids[BALL]))
	exit(1)

signal.signal(signal.SIGINT, handler)

if __name__ == '__main__':
	rospy.init_node('guesser', anonymous = 'True')
	pub_mask = rospy.Publisher("/guesser/image_mask", Image, queue_size=10)
	pub_bin = rospy.Publisher("guesser/image_bin", Image, queue_size=10)
	pub_annotated = rospy.Publisher("guesser/image_centroid", Image, queue_size=10)

	print("How many cups are in the game?")
	NUM_CUPS = int(input())
	assert NUM_CUPS > 0

	print("Which cup is the ping pong ball under?")
	BALL = int(input())
	assert BALL < NUM_CUPS

	observe()