from flask import Flask, request, render_template
import os
import sys

src_path = os.path.join(os.path.dirname(__file__), 'src')
sys.path.insert(0, src_path)

from mlengine.models.predict import Prediction, CustomData

application = Flask(__name__)

app = application

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/predict', methods=['GET', 'POST'])
def predict():
    if request.method == 'GET':
        return render_template(('prediction.html'))
    else:
        data = CustomData(
            gender=request.form.get('gender'),
            race_ethnicity=request.form.get('race_ethnicity'),
            parental_level_of_education=request.form.get('parental_level_of_education'),
            lunch=request.form.get('lunch'),
            test_preparation_course=request.form.get('test_preparation_course'),
            reading_score=int(request.form.get('reading_score')),
            writing_score=int(request.form.get('writing_score')),
        )

        pred_df = data.get_data_as_data_frame()
        prediction = Prediction()
        results = prediction.predict(pred_df)

        return render_template('prediction.html', results=results[0])

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)


"""
You are given an array of integers, where each element represents
the maximum number of steps that can be jumped going forward from
that element. Write a function to return the minimum number of jumps
you must take in order to get from the start to the end of the array.

For example,
    given [6, 2, 4, 0, 5, 1, 1, 4, 2, 9], you should return 2,
    as the optimal solution involves jumping from 6 to 5, and then from 5 to 9.
"""
from typing import List

def min_jumps(array: List[int]) -> int:
    size = len(array)

    if size <= 1:
        return 0
    if array[0] == 0:
        return -1

    jumps = 1
    index = 0
    steps = max_reach = array[0]

    while index < size - 1:
        max_reach = max(max_reach, index + array[index])

        if steps == 0:
            jumps += 1
            steps = max_reach - index

        if max_reach >= size - 1:
            return jumps + 1

        steps -= 1
        index += 1

        if index > max_reach:
            return -1

    return -1

if __name__ == "__main__":
    assert min_jumps([6, 2, 4, 0, 5, 1, 1, 4, 2, 9]) == 2
    assert min_jumps([1, 3, 5, 8, 9, 2, 6, 7, 6, 8, 9]) == 3


#!/usr/bin/python3

#imports
import conf
import requests
import json
from datetime import datetime, timedelta
from dateutil import parser
import pytz
import csv

#conf
conf.load("config.json")

#constants
UP_API_ENDPOINT = "https://api.up.com.au/api/v1"
TIME_NOW = pytz.timezone(conf.get("TIMEZONE")).localize(datetime.now())
TZ = pytz.timezone(conf.get("TIMEZONE"))
#DEBUG = not __debug__
DEBUG = False

#creds
UP_CREDS_HEADER = {"Authorization": "Bearer {}".format(conf.get("UP_API_KEY"))}

def main():
    #Main function ran on script startup
    if not testUpbankApi():
        exit(10)

    bankAccounts = getUpAccounts()

    days = getDays()

    print("Getting {} days of transactions".format(days))

    for account in bankAccounts:
        saveCsv(getTransactions(account, days), account)

def getDays():
    days = int(input("Enter how many days of transactions to grab: "))
    return days

def saveCsv(transactions,account):
    output = open("data/{}.csv".format(account['attributes']['displayName']), "w", encoding="utf-8")
    writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC)
    writer.writerow(["Date","Payee","Memo","Amount"])
    for transaction in transactions:
        writer.writerow([
            parser.parse(transaction["attributes"]["createdAt"]).strftime('%d-%m-%Y'),
            transaction["attributes"]["description"],
            "{}: {}".format(
                parser.parse(transaction['attributes']['createdAt']).astimezone(TZ).strftime('%I:%M%p'),
                transaction['attributes']['rawText'],
            ),
            transaction["attributes"]["amount"]["value"],
        ])

def printCsv(transactions,bankAccounts):
    print("Date,Payee,Memo,Amount")
    for transaction in transactions:
        print("{},{},{},{}".format(
            parser.parse(transaction["attributes"]["createdAt"]).strftime('%d-%m-%Y'),
            transaction["attributes"]["description"],
            "Imported: " + TIME_NOW.strftime('%d-%m-%Y'),
            transaction["attributes"]["amount"]["value"],
        ))

def testUpbankApi():
    '''
    Make a call to up bank's API and parse result to determine if key is correct
    '''
    apiStatus = False

    resp = requests.get("{}/util/ping".format(UP_API_ENDPOINT),headers=UP_CREDS_HEADER)

    if resp.status_code == 200:
        if DEBUG: print("Up Bank Connected: {}".format(json.loads(resp.content)["meta"]["statusEmoji"]))
        apiStatus = True
    if resp.status_code != 200:
        print("Up Bank connection failed: GET /util/ping {}".format(resp.status_code))
        raise ValueError("Up Bank credentials may be wrong. Move the config-template.json template to config.json and replace the U API credential")

    return apiStatus

def getUpAccounts():
    #Get a list of all up bank accounts aka savers
    resp = requests.get("{}/accounts".format(UP_API_ENDPOINT),headers=UP_CREDS_HEADER)
    accounts = []

    if resp.status_code == 200:
        if DEBUG: print("Found Up Bank Accounts: ")
        for account in json.loads(resp.content)["data"]:
            if DEBUG: print("\t{}".format(account["attributes"]["displayName"]))
            accounts.append(account)
    if resp.status_code != 200:
        print("Up Bank connection failed: GET /accounts {}".format(resp.status_code))

    return accounts

def getTransactions(account, daysFrom):
    #Get past x days of transactions from the cheque account, -1 gets all
    transactions = []

    nextTransactionPage = account["relationships"]["transactions"]["links"]["related"]
    while True:
        resp = requests.get(nextTransactionPage,headers=UP_CREDS_HEADER)

        for transaction in json.loads(resp.content)["data"]:
            transactions.append(transaction)

        #If the transactions list has no more items, stop getting more transactions
        if json.loads(resp.content)["links"]["next"] is None:
            break

        #If the first transaction in the list is before a certain date, stop getting more transactions
        if daysFrom != -1:
            if datetime.fromisoformat(json.loads(resp.content)["data"][0]["attributes"]["createdAt"]) < TIME_NOW + timedelta(days=-daysFrom):
                break

        nextTransactionPage = json.loads(resp.content)["links"]["next"]

    return removeOldTransactions(transactions, daysFrom)

def removeOldTransactions(transactions, daysFrom):
    filteredTransactions = []

    if daysFrom == -1:
        filteredTransactions = transactions
    else:
        for transaction in transactions:
            if datetime.fromisoformat(transaction["attributes"]["createdAt"]) >= TIME_NOW + timedelta(days=-daysFrom):
                filteredTransactions.append(transaction)

    return filteredTransactions

def getAccountName(bankAccounts,accountId):
    for account in bankAccounts:
        if account["id"] == accountId:
            return account["attributes"]["displayName"]

if __name__ == "__main__":
    main()


#!/usr/bin/env python

__author__ = 'apw'

# Standard library dependencies
import os, sys
import sys

# Third-party imports
import numpy as np
import sqlalchemy
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from math import radians

# Custom modules
from spectradb.HydraDR9dbConnection import db
from spectradb.BOSSModelClasses import *
from spectradb.SDSSModelClasses import *

# Query to get all SDSS-I and -II "good" plates
sdssPlateCenters = db.Session.query(Pointing.center_ra, Pointing.center_dec).\
                          join(Design, Plate, SDSSSpectrumHeader, SDSSPlateQuality).\
                          filter(SDSSPlateQuality.label == "good").\
                          all()

# Query to get all "good" BOSS plates from before July 2011 (DR9)
bossDR9PlateCenters = db.Session.query(Pointing.center_ra, Pointing.center_dec).\
                          join(Design, Plate, BOSSSpectrumHeader, BOSSPlateQuality).\
                          filter(BOSSSpectrumHeader.mjd <= 55811).\
                          filter(BOSSPlateQuality.label == "good").\
                          all()

# Create a figure and axis with an aitoff projection
fig = plt.figure(figsize=(15,10))
ax = fig.add_subplot(111, projection="aitoff")

for ii,(ra,dec) in enumerate(sdssPlateCenters):
    # For the first plate, add a label to be used for the legend
    if ii == 0: cir = Circle((radians(-(ra-180)),radians(dec)), radius=radians(1.5), facecolor='b', ec="None", alpha=0.3, label="SDSS-I,-II ('good')")
    else: cir = Circle((radians(-(ra-180)),radians(dec)), radius=radians(1.5), facecolor='b', ec="None", alpha=0.3)
    ax.add_patch(cir)

for ii,(ra,dec) in enumerate(bossDR9PlateCenters):
    # For the first plate, add a label to be used for the legend
    if ii == 0: cir = Circle((radians(-(ra-180)),radians(dec)), radius=radians(1.5), facecolor='r', ec="None", alpha=0.3, label="BOSS DR9")
    else: cir = Circle((radians(-(ra-180)),radians(dec)), radius=radians(1.5), facecolor='r', ec="None", alpha=0.3)
    ax.add_patch(cir)

# Add a grid to the plot
ax.grid()

# Matplotlib doesn't allow changing the axis limits on a projected plot, so we have to do this ourselves...
ax.set_xticklabels([330, 300, 270, 240, 210, 180, 150, 120, 90, 60, 30])
fig.suptitle("BOSS DR9 Plate Coverage")
ax.legend()

plt.savefig("boss_dr9.png", dpi=100)
#or, for a pdf: plt.savefig("boss_dr9.pdf")


from pathlib import Path
import os
from datetime import timedelta

# from .loc_settings import *

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent.parent

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'ckeditor',
    'ckeditor_uploader',

    'rest_framework',
    'rest_framework.authtoken',
    'djoser',

    'contest',
    'api',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'dycon_web.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates'),],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'dycon_web.wsgi.application'

# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

# AUTH_PASSWORD_VALIDATORS = [
#     {
#         'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
#     },
#     {
#         'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
#     },
#     {
#         'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
#     },
#     {
#         'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
#     },
# ]

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGIN_REDIRECT_URL='/competitions/'
ACCOUNT_ACTIVATION_DAYS = 365

# redis related settings
REDIS_PORT = '6379'
REDIS_SERVICE_HOST = os.environ.get("REDIS_SERVICE_HOST", "127.0.0.1")
BROKER_PORT = 1
RESULTS_PORT = 2

CELERY_BROKER_URL = f"redis://{REDIS_SERVICE_HOST}:{REDIS_PORT}/{BROKER_PORT}"
CELERY_BROKER_TRANSPORT_OPTIONS = {'visibility_timeout':3600}
CELERY_RESULT_BACKEND = (
    f"redis://{REDIS_SERVICE_HOST}:{REDIS_PORT}/{RESULTS_PORT}"
)
CELERY_ACCEPT_CONTENT = ['application/json']

CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'

CELERY_TASK_DEFAULT_QUEUE = "default"

CKEDITOR_UPLOAD_PATH = "uploads/"
CKEDITOR_CONFIGS = {
    'default': {
        'skin': 'moono',
        # 'skin': 'office2013',
        'toolbar_Basic': [
            ['Source', '-', 'Bold', 'Italic']
        ],
        'toolbar_YourCustomToolbarConfig': [
            {'name': 'document', 'items': ['Source', '-', 'Save', 'NewPage', 'Preview', 'Print', '-', 'Templates']},
            {'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
            {'name': 'editing', 'items': ['Find', 'Replace', '-', 'SelectAll']},
            {'name': 'forms',
             'items': ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
                       'HiddenField']},
            '/',
            {'name': 'basicstyles',
             'items': ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']},
            {'name': 'paragraph',
             'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-',
                       'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl',
                       'Language']},
            {'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
            {'name': 'insert',
             'items': ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']},
            '/',
            {'name': 'styles', 'items': ['Styles', 'Format', 'Font', 'FontSize']},
            {'name': 'colors', 'items': ['TextColor', 'BGColor']},
            {'name': 'tools', 'items': ['Maximize', 'ShowBlocks']},
            {'name': 'about', 'items': ['About']},
            '/',  # put this to force next toolbar on new line
            {'name': 'yourcustomtools', 'items': [
                # put the name of your editor.ui.addButton here
                'Preview',
                'Maximize',

            ]},
        ],
        'toolbar': 'YourCustomToolbarConfig',  # put selected toolbar config here
        # 'toolbarGroups': [{ 'name': 'document', 'groups': [ 'mode', 'document', 'doctools' ] }],
        # 'height': 291,
        # 'width': '100%',
        # 'filebrowserWindowHeight': 725,
        # 'filebrowserWindowWidth': 940,
        # 'toolbarCanCollapse': True,
        # 'mathJaxLib': '//cdn.mathjax.org/mathjax/2.2-latest/MathJax.js?config=TeX-AMS_HTML',
        'tabSpaces': 4,
        'extraPlugins': ','.join([
            'uploadimage',  # the upload image feature
            # your extra plugins here
            'div',
            'autolink',
            'autoembed',
            'embedsemantic',
            'autogrow',
            # 'devtools',
            'widget',
            'lineutils',
            'clipboard',
            'dialog',
            'dialogui',
            'elementspath',
        ]),
    }
}

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication', # store in database
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
}

DJOSER = {
    'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}',
    'USERNAME_RESET_CONFIRM_URL': '#/username/reset/confirm/{uid}/{token}',
    'ACTIVATION_URL': '#/activate/{uid}/{token}',
    'SEND_ACTIVATION_EMAIL': True,
    'SERIALIZERS': {},
}


import pyodbc
import json
import os
import qrcode

from flask import Flask, request, send_file, jsonify
from flask_restful import Api
from io import BytesIO

app = Flask(__name__)
api = Api(app)

# LOCAL
# with open('secrets.json') as f:
#     secrets = json.load(f)
# connection = pyodbc.connect(secrets['DATABASE_CONNECTION_STRING'])

# HOSTED
connection_string = os.environ.get('DATABASE_CONNECTION_STRING')
connection = pyodbc.connect(connection_string)

@app.route('/', methods=['GET'])
def get():
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM qr_code_data_db")
    rows = cursor.fetchall()

    qr_code_data = []
    for row in rows:
        qr_code_dic = {}
        for column in row.cursor_description:
            column_name = column[0]
            column_value = getattr(row, column_name)
            qr_code_dic[column_name] = column_value
        qr_code_data.append(qr_code_dic)

    return jsonify(qr_code_data)

@app.route('/data', methods=['POST'])
def post():
    cursor = connection.cursor()
    qr_code_data = request.get_json()

    qr_code_data_id = qr_code_data['QRCodeDataID']
    user_email = qr_code_data['UserEmail']
    cert_number = qr_code_data['CertNumber']
    scanned = qr_code_data['Scanned']
    expires = qr_code_data['Expires']
    latitude = qr_code_data['Latitude']
    longitude = qr_code_data['Longitude']

    query = """
        INSERT INTO qr_code_data_db
            (QRCodeDataID, UserEmail, CertNumber, Scanned, Expires, Latitude, Longitude)
        VALUES
            (?, ?, ?, ?, ?, ?, ?)
    """

    values = (qr_code_data_id, user_email, cert_number, scanned, expires, latitude, longitude)

    cursor.execute(query, values)
    connection.commit()

    return jsonify({"message": "Data added"}), 201

@app.route('/delete/<int:qr_code_data_id>', methods=['DELETE'])
def delete(qr_code_data_id):
    try:
        cursor = connection.cursor()
        cursor.execute("DELETE FROM qr_code_data_db WHERE QRCodeDataID = ?", qr_code_data_id)
        connection.commit()
        return jsonify({'message': 'Data deleted'}), 200
    except Exception as e:
        print(str(e))
        return jsonify({'message': 'Failed to delete data'}), 500

@app.route('/generate_qrcode', methods=['GET'])
def generate_qrcode():
    pdf_url = request.args.get('pdf_url')

    qr = qrcode.QRCode(version=7,
                       error_correction=qrcode.ERROR_CORRECT_L,
                       box_size=12,
                       border=2)
    qr.add_data(pdf_url)
    qr.make(fit=True)
    img = qr.make_image(fill='Black', back_color='White')

    img_bytes = BytesIO()
    img.save(img_bytes)
    img_bytes.seek(0)

    return send_file(img_bytes, mimetype='image/png')

if __name__ == '__main__':
    app.run(debug=True)


# -*- coding: utf-8 -*-
import scrapy
from scrapy_sample.items import ImageItem
from scrapy.http import Request
from scrapy.loader import ItemLoader
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

import copy
import re

class MzituSpider(scrapy.Spider):
    name = 'mzitu'
    urlpre = 'https://www.mzitu.com'
    start_urls = [
	        'https://www.mzitu.com',
	]
    header = { #'accept-encoding': 'gzip, deflate, br',
            #'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            #'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36' }
    def start_requests(self):
        return [Request(self.start_urls[0],headers=self.header,callback=self.parse)]
    def parse0(self,response):
        """ This function parses pages
            @url https://www.mzitu.com
            @scrapes https://www.mzitu.com/193336  https://www.mzitu.com/page/2/
        """
        print(response.request.headers)
        urls = response.xpath('//ul[@id="pins"]//li//span//@href').getall()
        for url in urls:
            print(url)
            yield  Request( url ,headers=self.header,callback=self.parse_article)
        page_urls= response.xpath('//div[@class="nav-links"]//a/@href').getall()
        for url in page_urls:
            yield  Request( url ,headers=self.header,callback=self.parse)

    def parse_article(self,response):
        l = ItemLoader(item=ImageItem(), response=response)
        l.add_xpath('image_urls', '//div[@class="main-image"]//a/img/@src')
        l.add_xpath('title', '//h2//text()')
        l.add_xpath('img_folder', '//h2//text()')
        # l.add_xpath('datetime', './/div[@class="metaLeft"]//div[@class="month_Year"]/text()')
        # count = response.xpath('//div[@class="pagenavi"]//a/span/text()').getall()[-2]
        l.add_value('referer', response.url)
        yield l.load_item()
        page_urls = response.xpath('//div[@class="pagenavi"]//a/@href').getall()
        for page in page_urls:
            yield  Request( page ,headers=self.header,callback=self.parse_image, meta={"img_folder":l.get_value("img_folder")})
    def parse_image(self,response):
        l = ItemLoader(item=ImageItem(), response=response)
        l.add_xpath('image_urls', '//div[@class="main-image"]//a/img/@src')
        l.add_xpath('title', '//h2//text()')
        l.add_value('img_folder', response.meta['img_folder'])
        # l.add_xpath('datetime', './/div[@class="metaLeft"]//div[@class="month_Year"]/text()')
        l.add_value('referer', response.url)
        return l.load_item()

class MzituSpider2(CrawlSpider):
    name = 'mzitu2'
    urlpre = 'https://www.mzitu.com'
    start_urls = [
            #'https://www.mzitu.com/193823',
            'https://www.mzitu.com/page/2/',

	]
    header = { #'accept-encoding': 'gzip, deflate, br',
            #'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            #'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36' }
    rules = (
        # Rule(LinkExtractor(allow='/\\d+/\\d+', restrict_xpaths='//div[@class="pagenavi"]'),callback='parse_nop3'),
        Rule(LinkExtractor(allow='/\\d+',restrict_xpaths='//ul[@id="pins"]//li//span'),callback='parse_article'), # from list get article
        Rule(LinkExtractor(allow='/page/\\d+/',restrict_xpaths='//div[@class="nav-links"]')), # next-page of list
    )
    routes = [  {"allow":"/\\d+$","callback":"parse_article"} ,
                    {"allow":"/page/\\d+/","callback":"parse_nop"} ,
        ]
    for r in routes:
        r.update(allow_res=re.compile(r["allow"]))
    def parse_start_url(self, response):
        for ar in self.routes:
            if ar["allow_res"].search(response.url):
                return getattr(self,  ar["callback"] ) (response)
        return self.parse_article(response)
    def parse_nop(self,response):
        """ This function parses pages
            @url https://www.mzitu.com
            @scrapes https://www.mzitu.com/193336  https://www.mzitu.com/page/2/
        """
    def parse_article(self,response):
        """   @url  https://www.mzitu.com/193823/
              @scrapes  https://www.mzitu.com/193823/2

        """
        l = ItemLoader(item=ImageItem(), response=response)
        l.add_xpath('image_urls', '//div[@class="main-image"]//a/img/@src')
        l.add_xpath('title', '//h2//text()')
        l.add_xpath('img_folder', '//h2//text()')
        l.add_xpath('datetime', '//div[@class="main-meta"]/span',re='[\\d :-]{8,}')
        l.add_value('referer', response.url)
        lt = l.load_item()
        yield lt
        count = int( (response.xpath('//div[@class="pagenavi"]//a/span/text()').getall() or [0,0] )[-2] )
        print("count",count,)
        # page_urls = response.xpath('//div[@class="pagenavi"]//a/@href').getall()
        for i in range(2, count):
            page = '{0}/{1}'.format(response.url, i)
            yield  Request( page ,headers=self.header,callback=self.parse_image,meta={"item":lt })
    def parse_image(self,response):
        it = copy.copy( response.meta['item'])
        it['referer'] =  response.url
        it["image_urls"] = response.xpath('//div[@class="main-image"]//a/img/@src').getall()# "https://i5.meizitu.net/2019/07/07c02.jpg"
        return it


"""Profile model."""

# Django
from django.db import models

# Utilities
from utils.models import GymModel

class Profile(GymModel):
    """Profile model.

    A profile holds a users public data like,
    picture, features and if it is active."""

    user = models.OneToOneField('users.User', on_delete=models.CASCADE)
    picture = models.ImageField(
        'profile picture',
        upload_to='users/pictures/',
        blank=True,
        null=True
    )

    age = models.PositiveSmallIntegerField(null=True, blank=True)

    weight = models.FloatField(
        'user weight',
        default=0,
        help_text='user weight in float.'
    )

    height = models.PositiveIntegerField(
        'user height',
        default=0,
       help_text= 'user height in float.'
    )

    is_active = models.BooleanField(
        default=False,
        help_text='Only active users can have gym services.'
    )

    def __str__(self):
        """Return user's str representation."""
        return str(self.user)


import asyncio
from engine.codec.proto_importer import PlayerLoginRequest,CreateRoleRequest, SpeakOnWorldRequest
from engine.codec.Codec import CodecMgr
from engine.utils.Utils import uint_from_bytes, uint32_to_bytes
from engine.utils.Singleton import Singleton
import uuid

class ClientMessage:
    __slots__ = ["msg_id", "data_len", "data"]

    def __init__(self):
        self.msg_id = 0
        self.data_len = 0
        self.data = bytearray()

    def __str__(self):
        return f"msg_id:{self.msg_id}, self.data_len:{self.data_len}, data:{self.data}"

class ClientMsgPack(Singleton):
    def __init__(self):
        pass

    def pack(self, mess:ClientMessage):
        data = bytearray()
        data += uint32_to_bytes(mess.msg_id)
        data += uint32_to_bytes(len(mess.data))
        data += mess.data
        return data

    def unpack(self, data, index):
        mess = ClientMessage()
        mess.msg_id = uint_from_bytes(data[index:index+4])
        mess.data_len = uint_from_bytes(data[index+4:index+8])
        mess.data = data[index+8:index+8+mess.data_len]
        index+8+mess.data_len
        return mess, index

class EchoClientProtocol(asyncio.Protocol):
    def __init__(self):
        self.transport = None

    def connection_made(self, transport):
        print(f'connection_made: {transport}')

    def data_received(self, data):
        mess, index = ClientMsgPack().unpack(data, 0)
        cmd = CodecMgr().get_proto_name(mess.msg_id)
        obj = CodecMgr().decode(cmd, mess.data)
        print(f"receive:{cmd}, data:{obj}")

    def connection_lost(self, exc):
        print('The server closed the connection')

def send_pack(transport, pck):
    msg_id = CodecMgr().get_proto_id(pck.DESCRIPTOR.name)
    mess = ClientMessage()
    mess.msg_id = msg_id
    mess.data = CodecMgr().encode(pck)
    data = ClientMsgPack().pack(mess)
    transport.write(data)

def send_player_login(player_id, transport):
    req = PlayerLoginRequest()
    req.player_id = player_id
    send_pack(transport, req)
    print(f"player:{player_id} send login")

def send_create_role(player_id, transport):
    req = CreateRoleRequest()
    req.player_id = player_id
    req.role_id = 1010
    send_pack(transport, req)
    print(f"player:{player_id} send create role:{req.role_id}")

def send_speak_world(player_id, name, transport):
    req = SpeakOnWorldRequest()
    req.player_id = player_id
    req.name = name
    req.content = "hello, i'm wind!"
    send_pack(transport, req)
    print(f"player:{player_id} speak world:{req.content}")

async def main():
    # Get a reference to the event loop as we plan to use
    # low-level APIs.
    loop = asyncio.get_running_loop()
    transport, protocol = await loop.create_connection(
        lambda: EchoClientProtocol(),
        '127.0.0.1', 50100)

    await asyncio.sleep(3)
    player_id = uuid.uuid4().hex
    name = "name_"+player_id[-4:]
    send_player_login(player_id, transport)
    await asyncio.sleep(1)

    send_create_role(player_id, transport)
    await asyncio.sleep(1)

    while True:
        send_speak_world(player_id, name, transport)
        await asyncio.sleep(2)

asyncio.run(main())


#!/usr/bin/env python
from __future__ import division
import rospy
from pidrone_pkg.msg import RC
from geometry_msgs.msg import Pose, PoseStamped
import time
import tf
import math
import numpy as np
from copy import deepcopy
import sys

#####################################################
#						PID							#
#####################################################
class PIDaxis():
    def __init__(self, kp, ki, kd, kp_upper = None, i_cap = None, d_cap = None, control_range = (1000,2000), midpoint = 1500, smoothing = True):
        # tuning
        self.kp = kp
        self.ki = ki
        self.kd = kd
        # config
        self.kp_upper = kp_upper
        self.i_cap = i_cap
        self.d_cap = d_cap
        self.control_range = control_range
        self.midpoint = midpoint
        self.smoothing = True
        # internal
        self._old_err = None
        self._p = 0
        self._i = 0
        self._d = 0
        self._dd = 0
        self._ddd = 0
        self._t = None

    def step(self, err):
        if self._t is None: time_elapsed = 0.001 # first time around prevent time spike
        else: time_elapsed = time.time() - self._t
        self._t = time.time()
        if self._old_err is None: self._old_err = err # first time around prevent d term spike

        # find the p,i,d components
        if self.kp_upper is not None and err < 0:
            self._p = err * self.kp_upper
        else:
            self._p = err * self.kp

        self._i += err * self.ki * time_elapsed
        if self.i_cap is not None and np.absolute(self._i) > self.i_cap:
            self._i = np.sign(self._i) * self.i_cap

        self._d = (err - self._old_err) * self.kd / time_elapsed
        if self.d_cap is not None and np.absolute(self._d) > self.d_cap:
            self._d = np.sign(self._d) * self.d_cap
        self._old_err = err

        # smooth over the last three d terms
        if self.smoothing:
            self._d = (self._d * 8.0 + self._dd * 5.0 + self._ddd * 2.0)/15.0
            self._ddd = self._dd
            self._dd = self._d

        raw_output = self._p + self._i + self._d
        output = min(max(raw_output + self.midpoint, self.control_range[0]),
                self.control_range[1])

        return output

class PID:
    def __init__(self,
        roll = PIDaxis(1.1, 0.25, 0.250),
        pitch = PIDaxis(1.1, 0.25, 0.250),
        yaw = PIDaxis(500 * 0, 0,0),
        throttle = PIDaxis(10.0, 40 * 0, 0.7, kp_upper=0, d_cap=300, midpoint =
            1200)):
        self.roll = roll
        self.pitch = pitch
        self.yaw = yaw
        self.throttle = throttle
        self.sp = None

    def update_setpoint(self, data):
        self.sp = data

    def step(self, pos):
        if self.sp is None:
            return [1500, 1500, 1500, 1000]
        else:
            err = self.calc_err(pos)
            cmd_r = self.roll.step(err[0])
            cmd_p = self.pitch.step(err[1])
            cmd_y = self.yaw.step(err[2])
            cmd_t = self.throttle.step(err[3])

            return [cmd_r, cmd_p, cmd_y, cmd_t]

    def quat_to_rpy(self, q):
        """ takes in a quaternion (like from a pose message) and returns (roll, pitch, yaw) """
        return tf.transformations.euler_from_quaternion([q.x, q.y, q.z, q.w])

    def calc_err(self, pos):
        """ given a position and a set point (in global coordinates), this calculates the error
        in the drone's coordinate system (r,p,y,t) """
        _, _, pos_yaw = self.quat_to_rpy(pos.pose.orientation)
        _, _, sp_yaw  = self.quat_to_rpy(self.sp.pose.orientation)

        err_x = self.sp.pose.position.x - pos.pose.position.x
        err_y = self.sp.pose.position.y - pos.pose.position.y
        err_z = self.sp.pose.position.z - pos.pose.position.z
        err_yaw = sp_yaw - pos_yaw

        sp_angle = -np.arctan2(err_x, err_y) # the angle of the drone's pos relative to the setpoint
        sp_norm = np.sqrt(err_x**2 + err_y**2) # the distance from the drone to the setpoint (in the plane)
        diff_angle = pos_yaw - sp_angle # the difference between the drone's yaw and its sp_angle

        err_fb = np.cos(diff_angle) * sp_norm
        err_lr = np.sin(diff_angle) * sp_norm
        return (err_lr, err_fb, err_yaw, err_z)


"""Functions for building a simplex centroid design."""

import dexpy.design as design
import pandas as pd
import numpy as np
import itertools
from dexpy.eval import count_n_choose_k as count_nk

def build_simplex_centroid(factor_count):
    """Builds a Simplex Centroid mixture design.

    This mixture design can be used for 3 to 8 components. A simplex-centroid
    design consists of all points that are equally weighted mixtures of 1 to q
    components. Included are permutations of:

        Pure blends: (1, 0, ...,0)

        Binary blends: (1/2, 1/2, 0, ...,0)

        Tertiary blends: (1/3, 1/3, 1/3, 0, ...,0)

    and so on to the overall centroid: (1/q, 1/q, ..., 1/q).

    This design differs from a simplex-lattice design. It cannot be used to
    estimate the full cubic model, but can be used to estimate a special cubic
    model.

    :param factor_count: The number of mixture components to build for.
    :type factor_count: int
    """
    run_count = 0
    for i in range(1, factor_count+1):
        run_count += count_nk(factor_count, i)

    factor_names = design.get_factor_names(factor_count)
    factor_data = pd.DataFrame(0, columns=factor_names,
                                  index=np.arange(0, run_count))

    row = 0
    for i in range(1, factor_count+1):
        proportion = 1 / i
        for combo in itertools.combinations(factor_names, i):
            factor_data.loc[row, combo] = proportion
            row += 1

    return factor_data


import argparse
import pickle
from pathlib import Path

import data
import models
import models.networks as networks
import torch

class BaseOptions():
    def __init__(self):
        self.initialized = False

    def initialize(self, parser):
        # experiment specifics
        parser.add_argument('--name', type=str, default='debug',
                            help='identifier of the experiment')

        parser.add_argument('--gpu_ids', type=str, default='0',
                            help='gpu ids: e.g. 0  0,1,2, 0,2. use -1 for CPU')
        parser.add_argument('--checkpoints_dir', type=str,
                            default='../checkpoints', help='models are saved here')
        parser.add_argument('--model', type=str, default='decomposition',
                            choices=['detect', 'decomposition'])
        parser.add_argument('--norm_G', type=str, default='batch',
                            help='instance normalization or batch normalization')
        parser.add_argument('--norm_D', type=str, default='batch',
                            help='instance normalization or batch normalization')
        parser.add_argument('--phase', type=str,
                            default='train', help='train, val, test, etc')

        # input/output sizes
        parser.add_argument('--batch_size', type=int,
                            default=4, help='input batch size')
        parser.add_argument('--preprocess_mode', type=str, default='resize_and_crop', help='scaling and cropping of images at load time.', choices=(
            "resize_and_crop", "crop", "scale_width", "scale_width_and_crop", "scale_shortside", "scale_shortside_and_crop", "fixed", "none"))
        parser.add_argument('--load_size', type=int, default=256,
                            help='Scale images to this size. The final image will be cropped to --crop_size.')
        parser.add_argument('--crop_size', type=int, default=256,
                            help='Crop to the width of crop_size (after initially scaling the images to load_size.)')
        parser.add_argument('--aspect_ratio', type=float, default=1.0,
                            help='The ratio width/height. The final height of the load image will be crop_size/aspect_ratio')
        parser.add_argument('--input_nc', type=int, default=3,
                            help='# of input label classes without unknown class. If you have unknown class as class label, specify --contain_dopntcare_label.')
        parser.add_argument('--output_nc', type=int, default=3,
                            help='# of output image channels')

        # for setting inputs
        parser.add_argument('--dataset_root', type=str, default='../datasets')
        parser.add_argument('--dataset_mode', type=str, default='paired')
        parser.add_argument('--serial_batches', action='store_true',
                            help='if true, takes images in order to make batches, otherwise takes them randomly')
        parser.add_argument('--no_flip', action='store_true',
                            help='if specified, do not flip the images for data argumentation')
        parser.add_argument('--num_threads', default=8,
                            type=int, help='# threads for loading data')

        # for displays
        parser.add_argument('--display_winsize', type=int,
                            default=256, help='display window size')

        # for generator
        parser.add_argument(
            '--netG', type=str, default='spm', choices=['bdrar', 'dsd', 'spm'])
        parser.add_argument('--ngf', type=int, default=64,
                            help='# of gen filters in first conv layer')
        parser.add_argument('--init_type', type=str, default='xavier',
                            help='network initialization [normal|xavier|kaiming|orthogonal]')
        parser.add_argument('--init_variance', type=float, default=0.02,
                            help='variance of the initialization distribution')

        # for our model
        parser.add_argument('--mask_to_G', default=None,
                            help="Type of pre-computed mask feeded to G")
        parser.add_argument('--mask_to_G_thresh', type=float, default=0.0,
                            help="if <= 0.0, do nothing")
        # parser.add_argument('--use_precomp_mask_to_D', action='store_true')
        parser.add_argument('--fit_affine', type=str, default=None)
        parser.add_argument('--mask_preprocess_mode', type=str,
                            default="scale_width")

        self.initialized = True
        return parser

    def gather_options(self):
        # initialize parser with basic options
        if not self.initialized:
            parser = argparse.ArgumentParser(
                formatter_class=argparse.ArgumentDefaultsHelpFormatter)
            parser = self.initialize(parser)

        # get the basic options
        opt, unknown = parser.parse_known_args()

        # modify model-related parser options
        model_name = opt.model
        model_option_setter = models.get_option_setter(model_name)
        parser = model_option_setter(parser, self.isTrain)

        # modify dataset-related parser options
        dataset_mode = opt.dataset_mode
        dataset_option_setter = data.get_option_setter(dataset_mode)
        parser = dataset_option_setter(parser, self.isTrain)

        # modify networks-related parser options
        parser = networks.modify_commandline_options(parser, self.isTrain)

        opt, unknown = parser.parse_known_args()

        opt = parser.parse_args()
        self.parser = parser
        return opt

    def print_options(self, opt):
        message = ''
        message += '----------------- Options ---------------\n'
        for k, v in sorted(vars(opt).items()):
            comment = ''
            default = self.parser.get_default(k)
            if v != default:
                comment = '\t[default: %s]' % str(default)
            message += '{:>25}: {:<30}{}\n'.format(str(k), str(v), comment)
        message += '----------------- End -------------------'
        print(message)

    def option_file_path(self, opt, makedir=False):
        expr_dir = Path(opt.checkpoints_dir) / opt.name
        if makedir:
            expr_dir.mkdir(parents=True, exist_ok=True)
        file_name = expr_dir / 'opt.txt'
        return file_name

    def save_options(self, opt):
        file_name = self.option_file_path(opt, makedir=True)
        with file_name.open(mode='wt') as opt_file:
            for k, v in sorted(vars(opt).items()):
                comment = ''
                default = self.parser.get_default(k)
                if v != default:
                    comment = '\t[default: %s]' % str(default)
                opt_file.write('{:>25}: {:<30}{}\n'.format(
                    str(k), str(v), comment))

    def update_options_from_file(self, parser, opt):
        new_opt = self.load_options(opt)
        for k, v in sorted(vars(opt).items()):
            if hasattr(new_opt, k) and v != getattr(new_opt, k):
                new_val = getattr(new_opt, k)
                parser.set_defaults(**{k: new_val})
        return parser

    def load_options(self, opt):
        file_name = self.option_file_path(opt, makedir=False)
        new_opt = pickle.load(open(file_name + '.pkl', 'rb'))
        return new_opt

    def parse(self, save=False):

        opt = self.gather_options()
        opt.isTrain = self.isTrain   # train or test

        self.print_options(opt)
        if opt.isTrain:
            self.save_options(opt)

        # set gpu ids
        str_ids = opt.gpu_ids.split(',')
        opt.gpu_ids = []
        for str_id in str_ids:
            id = int(str_id)
            if id >= 0:
                opt.gpu_ids.append(id)
        if len(opt.gpu_ids) > 0:
            torch.cuda.set_device(opt.gpu_ids[0])

        assert len(opt.gpu_ids) == 0 or opt.batch_size % len(opt.gpu_ids) == 0, \
            "Batch size %d is wrong. It must be a multiple of # GPUs %d." \
            % (opt.batchSize, len(opt.gpu_ids))

        self.opt = opt
        return self.opt


import os
from typing import Union
import datetime
import json
from dateutil.relativedelta import relativedelta
from enum_keys import *

class TiempoEn_Años_Meses_Dias():
    def __init__(self, _años:int=0, _meses:int=0, _dias:int=0):
        self.años = _años
        self.meses = _meses
        self.dias = _dias

    def __str__(self):
        return '...{} año(s), {} mes(es) y {} día(s)...'.format(self.años, self.meses, self.dias)

class MontoDePena(TiempoEn_Años_Meses_Dias):
    def __init__(self,
    _años: int = 0,
    _meses: int = 0,
    _dias: int = 0,
    es_perpetua: bool = False,
    esDeEjecucionCondicional:bool=False,
    hayReclusionIndetArt52CP:bool=False,
    _plazoControlAños:int=0,
    _plazoControlMeses:int=0,
    _plazoControlDias:int=0,
    _esReincidente:bool=False,
    _esPorDelitosExcluidosLey27375:bool=False,
    _esPorDelitosExcluidosLey25892:bool=False,
    _esPorDelitosExcluidosLey25948:bool=False):
        super().__init__(_años, _meses, _dias)
        self.reincidencia = _esReincidente
        self.perpetua = es_perpetua
        self.ejecucionCondicional = esDeEjecucionCondicional
        self.reclusionPorTiempoIndeterminado = hayReclusionIndetArt52CP
        self.delitosExcluidosLey25948 = _esPorDelitosExcluidosLey25948
        self.delitosExcluidosLey25892 = _esPorDelitosExcluidosLey25892
        self.delitosExcluidosLey27375 = _esPorDelitosExcluidosLey27375
        self.plazoControl_años = _plazoControlAños
        self.plazoControl_meses = _plazoControlMeses
        self.plazoControl_dias = _plazoControlDias

        warning = self.perpetua + self.ejecucionCondicional + self.reclusionPorTiempoIndeterminado + self.reincidencia
        if warning > 1 and self.ejecucionCondicional:
            print('[[[ADVERTENCIA: Se ingresó pena de ejecución condicional junto con otra circunstancia incompatible (reincidencia, pena perpetua o reclusión por tiempo indeterminado)]]]')

        if self.perpetua:
            self.años = self.meses = self.dias = 0

    def _Return_TiempoEn_Años_Meses_Dias(self):
        return TiempoEn_Años_Meses_Dias(self.años, self.meses, self.dias)

    def __str__(self):
        if self.perpetua:
            return 'Es una pena perpetua'
        'La pena es de {} año(s), {} mes(es) y {} día(s).'.format(self.años, self.meses, self.dias)
        if self.reclusionPorTiempoIndeterminado:
            return 'La pena es de {} año(s), {} mes(es) y {} día(s), con la accesoria de reclusión por tiempo indeterminado (art. 52 CP).'.format(self.años, self.meses, self.dias)
        else:
            return 'La pena es de {} año(s), {} mes(es) y {} día(s).'.format(self.años, self.meses, self.dias)

class OtraDetencion():

    # class OtraDetencion():  CLASS VIEJA
#     def __init__(self, fecha_de_detencion:datetime.datetime, fecha_de_libertad:datetime.datetime, nombre:str="Sin nombre"):
#         self._nombre = nombre
#         self._detencion = fecha_de_detencion
#         self._libertad = fecha_de_libertad
#         self._tiempoDeDetencion = MontoDePena()
#         self._CalcularTiempoDeDetencion()

#     def _CalcularTiempoDeDetencion(self):
#         if FechaA_es_Mayor_Que_FechaB(self._detencion, self._libertad):
#             raise Exception('ERROR: Se ingresó una fecha de detención posterior a la fecha de libertad.')

#         fecha_temp = self._detencion
#         _meses = 0
#         _años = 0
#         _dias = 1

#         if self._detencion.day > self._libertad.day:
#             _meses -= 1

#         while fecha_temp.month != self._libertad.month:
#             _meses += 1
#             fecha_temp += relativedelta(months=1)

#         while fecha_temp.year != self._libertad.year:
#             _años += 1
#             fecha_temp += relativedelta(years=1)

#         while fecha_temp.day != self._libertad.day:
#             _dias += 1
#             fecha_temp += relativedelta(days=1)

#         self._tiempoDeDetencion.años = _años
#         self._tiempoDeDetencion.meses = _meses
#         self._tiempoDeDetencion.dias = _dias

#         print('Este tiempo de detención es de: {}'.format(self._tiempoDeDetencion))

    def __init__(self, fecha_de_detencion:datetime.datetime, fecha_de_libertad:datetime.datetime, nombre:str="Sin nombre"):
        self._nombre = nombre
        self._detencion = fecha_de_detencion
        self._libertad = fecha_de_libertad
        self._tiempoDeDetencion = MontoDePena()
        self._CalcularTiempoDeDetencion()

    def _CalcularTiempoDeDetencion(self):
        if FechaA_es_Mayor_Que_FechaB(self._detencion, self._libertad):
            raise Exception('ERROR: Se ingresó una fecha de detención posterior a la fecha de libertad.')

        if FechaA_es_Igual_Que_FechaB(self._detencion, self._libertad):
            self._tiempoDeDetencion.dias = 1
            print('Este tiempo de detención es de {} año(s), {} mes(es) y {} día(s)'.format(self._tiempoDeDetencion.años, self._tiempoDeDetencion.meses, self._tiempoDeDetencion.dias))
            return

        fecha_temp = self._detencion
        _meses = 0
        _años = 0
        _dias = 0
        se_paso = False

        # Primero suma meses/años y los acumula hasta que se pase de la fecha de libertad, o que sean iguales
        while se_paso == False:
            fecha_previa = fecha_temp
            fecha_temp += relativedelta(months=1)
            if (FechaA_es_Mayor_Que_FechaB(fecha_temp, self._libertad) == False
            or FechaA_es_Igual_Que_FechaB(fecha_temp, self._libertad) == True):
                _meses += 1
                if _meses == 12:
                    _meses = 0
                    _años += 1
            else:
                se_paso = True
                fecha_temp = fecha_previa

        # Luego, hace lo mismo con los días
        se_paso = False
        while se_paso == False:
            fecha_previa = fecha_temp
            fecha_temp += relativedelta(days=1)
            if (FechaA_es_Mayor_Que_FechaB(fecha_temp, self._libertad) == False
            or FechaA_es_Igual_Que_FechaB(fecha_temp, self._libertad) == True):
                _dias += 1
            else:
                se_paso = True

        self._tiempoDeDetencion.años = _años
        self._tiempoDeDetencion.meses = _meses
        self._tiempoDeDetencion.dias = _dias

        # Aplica correcciones
        if (self._tiempoDeDetencion.años == 0
        and self._tiempoDeDetencion.meses == 0):
            self._tiempoDeDetencion.dias += 1

        print('Este tiempo de detención es de {} año(s), {} mes(es) y {} día(s)'.format(self._tiempoDeDetencion.años, self._tiempoDeDetencion.meses, self._tiempoDeDetencion.dias))

class RegimenNormativoAplicable():
    def __init__(self, _fechaDelHecho:datetime.date):

        # CARGA LOS ARCHIVOS JSON EN VARIABLES

        with open('Regimenes/libertadCondicional.json', encoding='utf-8') as reg_LC:
            self._JSON_LC = json.load(reg_LC)

        with open('Regimenes/salidasTransitorias.json', encoding='utf-8') as reg_ST:
            self._JSON_ST = json.load(reg_ST)

        with open('Regimenes/libertadAsistida.json', encoding='utf-8') as reg_LA:
            self._JSON_LA = json.load(reg_LA)

        with open('Regimenes/regimenPrepLib.json', encoding='utf-8') as reg_PrepLib:
            self._JSON_PREPLIB = json.load(reg_PrepLib)

        with open('Regimenes/unidadesFijas.json', encoding='utf-8') as reg_unidadesFijas:
            self._JSON_UNIDADESFIJAS = json.load(reg_unidadesFijas)
            # En este JSON, la información de vigencia de la Resolución MSEG 270/2016 corresponde a la de la
            # ley 27.302, porque desde esa fecha aplica el régimen de las Unidades Fijas. Los formularios
            # ya existían de antes

        with open('Regimenes/delitosExcluidosLib.json', encoding='utf-8') as textoDelitosExcluidosLib:
            self._JSON_DELITOSEXCLUIDOSLIB = json.load(textoDelitosExcluidosLib)

        # DETERMINA RÉGIMEN DE LIBERTAD CONDICIONAL

        self._regimen_LC = 'No aplica'
        for key in self._JSON_LC:
            fecha_implementacion = datetime.date(self._JSON_LC[key][LC_KEYS._fechaImplementacion_YEAR_KEY.value], self._JSON_LC[key][LC_KEYS._fechaImplementacion_MONTH_KEY.value], self._JSON_LC[key][LC_KEYS._fechaImplementacion_DAY_KEY.value])
            if FechaA_es_Mayor_O_Igual_Que_FechaB(_fechaDelHecho, fecha_implementacion):
                self._regimen_LC = key

        # DETERMINA RÉGIMEN DE SALIDAS TRANSITORIAS

        self._regimen_ST = 'No aplica'
        for key in self._JSON_ST:
            fecha_implementacion = datetime.date(self._JSON_ST[key][ST_KEYS._fechaImplementacion_YEAR_KEY.value], self._JSON_ST[key][ST_KEYS._fechaImplementacion_MONTH_KEY.value], self._JSON_ST[key][ST_KEYS._fechaImplementacion_DAY_KEY.value])
            if FechaA_es_Mayor_O_Igual_Que_FechaB(_fechaDelHecho, fecha_implementacion):
                self._regimen_ST = key

        # DETERMINA RÉGIMEN DE LIBERTAD ASISTIDA

        self._regimen_LA = 'No aplica'
        for key in self._JSON_LA:
            fecha_implementacion = datetime.date(self._JSON_LA[key][LA_KEYS._fechaImplementacion_YEAR_KEY.value], self._JSON_LA[key][LA_KEYS._fechaImplementacion_MONTH_KEY.value], self._JSON_LA[key][LA_KEYS._fechaImplementacion_DAY_KEY.value])
            if FechaA_es_Mayor_O_Igual_Que_FechaB(_fechaDelHecho, fecha_implementacion):
                self._regimen_LA = key

        # DETERMINA RÉGIMEN DE PREPARACIÓN PARA LA LIBERTAD

        self._regimen_PREPLIB = 'No aplica'
        for key in self._JSON_PREPLIB:
            fecha_implementacion = datetime.date(self._JSON_PREPLIB[key][REGPREPLIB_KEYS._fechaImplementacion_YEAR_KEY.value], self._JSON_PREPLIB[key][REGPREPLIB_KEYS._fechaImplementacion_MONTH_KEY.value], self._JSON_PREPLIB[key][REGPREPLIB_KEYS._fechaImplementacion_DAY_KEY.value])
            if FechaA_es_Mayor_O_Igual_Que_FechaB(_fechaDelHecho, fecha_implementacion):
                self._regimen_PREPLIB = key

        # DETERMINA RÉGIMEN DE UNIDADES FIJAS

        self._regimen_UNIDADESFIJAS = 'No aplica'
        for key in self._JSON_UNIDADESFIJAS:
            fecha_implementacion = datetime.date(self._JSON_UNIDADESFIJAS[key][UNIDADESFIJAS_KEYS._fechaImplementacion_YEAR_KEY.value], self._JSON_UNIDADESFIJAS[key][UNIDADESFIJAS_KEYS._fechaImplementacion_MONTH_KEY.value], self._JSON_UNIDADESFIJAS[key][UNIDADESFIJAS_KEYS._fechaImplementacion_DAY_KEY.value])
            if FechaA_es_Mayor_O_Igual_Que_FechaB(_fechaDelHecho, fecha_implementacion):
                self._regimen_UNIDADESFIJAS = key

    def LIBERTAD_CONDICIONAL(self, ask:LC_KEYS):
        return self._JSON_LC[self._regimen_LC][ask.value]

    def SALIDAS_TRANSITORIAS(self, ask:ST_KEYS):
        return self._JSON_ST[self._regimen_ST][ask.value]

    def LIBERTAD_ASISTIDA(self, ask:LA_KEYS):
        return self._JSON_LA[self._regimen_LA][ask.value]

    def REGIMEN_PREPARACION_LIBERTAD(self, ask:REGPREPLIB_KEYS):
        return self._JSON_PREPLIB[self._regimen_PREPLIB][ask.value]

    def UNIDADES_FIJAS(self, ask:UNIDADESFIJAS_KEYS):
        return self._JSON_UNIDADESFIJAS[self._regimen_UNIDADESFIJAS][ask.value]

    def DELITOS_EXCLUIDOS_LIBERTAD(self, ask:DELITOS_EXCLUIDOS_LIB_KEYS):
        return self._JSON_DELITOSEXCLUIDOSLIB[ask.value]

    def _ImprimirOutput(self, imprimir_reg_unidadesFijas:bool=False):
        print(Separadores._separadorComun)
        print('REGIMEN LEGAL APLICABLE')
        print('-----------------------')
        print(f' - Libertad condicional: {self._regimen_LC}')
        print(f' - Salidas transitorias: {self._regimen_ST}')
        print(f' - Libertad asistida: {self._regimen_LA}')
        print(f' - Régimen preparatorio para la liberación: {self._regimen_PREPLIB}')
        if imprimir_reg_unidadesFijas:
            print(f' - Unidades fijas: {self.UNIDADES_FIJAS(UNIDADESFIJAS_KEYS._denominacion_KEY)}')

    def _ArmarSTRING(self, imprimir_reg_unidadesFijas:bool=False):
        treturn = []
        treturn.append(Separadores._separadorComun)
        treturn.append('REGIMEN LEGAL APLICABLE')
        treturn.append('-----------------------')
        treturn.append(f' - Libertad condicional: {self._regimen_LC}')
        treturn.append(f' - Salidas transitorias: {self._regimen_ST}')
        treturn.append(f' - Libertad asistida: {self._regimen_LA}')
        treturn.append(f' - Régimen preparatorio para la liberación: {self._regimen_PREPLIB}')
        if imprimir_reg_unidadesFijas:
            treturn.append(f' - Unidades fijas: {self.UNIDADES_FIJAS(UNIDADESFIJAS_KEYS._denominacion_KEY)}')
        return treturn

class Preguntas_Input_Pena_TemporalOPerpetua():
    def __init__(self, pena_perpetua:bool=False):

        # Argumentos
        self._ARGUMENTOS_pena_perpetua = pena_perpetua

        self._fecha_del_hecho = 'NULL'
        self._regimen_normativo = 'NULL'
        self._fecha_de_detencion = 'NULL'
        self._monto_de_pena = 'NULL'
        self._monto_multa = 'NULL'
        self._otras_detenciones = 'NULL'
        self._estimulo_educativo = 'NULL'

        self._fecha_inicio_ejecucion = 'NULL'
        self._esta_ejecutando_pena = False
        self._fecha_ingreso_periodo_de_prueba = 'NULL'
        self._fecha_calificacion_BUENO = 'NULL'
        self._fecha_calificacion_EJEMPLAR = 'NULL'
        self._vuelve_a_restar_otras_detenciones_y_140_en_ST = False

        self.GetConsoleInput_HacerPreguntas()

    def GetConsoleInput_HacerPreguntas(self):

        # Si es por una perpetua, primero pregunta por la accesoria de reclusión por tiempo indeterminado, ya que
        # si se aplicó, las demás preguntas y cálculos no tendrían sentido
        if self._ARGUMENTOS_pena_perpetua:
            print(Separadores._separadorComun)
            user_input_52 = input('¿Se aplicó la accesoria del 52 CP (reclusión por tiempo indeterminado)? (S/N): ')
            if user_input_52 == "S" or user_input_52 == "s":
                self._monto_de_pena = MontoDePena(es_perpetua=True)
                self._monto_de_pena.reclusionPorTiempoIndeterminado = True
                print(' - Se aplicó la accesoria de reclusión por tiempo indeterminado.')
                return
            else:
                print(' - No hay accesoria de reclusión por tiempo indeterminado.')

        # Fecha del hecho y cálculo del régimen normativo
        print(Separadores._separadorComun)
        self._fecha_del_hecho = GetConsoleInput_Fecha('Ingresar la fecha del hecho: ')
        self._regimen_normativo = RegimenNormativoAplicable(self._fecha_del_hecho)
        print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_del_hecho)}')

        # Fecha de detención
        print(Separadores._separadorComun)
        self._fecha_de_detencion = GetConsoleInput_Fecha('Ingresar fecha de detención: ')
        print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_de_detencion)}')

        # Monto de pena
        if self._ARGUMENTOS_pena_perpetua:
            self._monto_de_pena = MontoDePena(es_perpetua=True)
        else:
            print(Separadores._separadorComun)
            self._monto_de_pena = GetConsoleInput_MontoDePena_temporal()
            print(f' - La pena ingresada es de {self._monto_de_pena.años} año(s), {self._monto_de_pena.meses} mes(es), {self._monto_de_pena.dias} día(s).')

        # Unidades Fijas
        if self._regimen_normativo._regimen_UNIDADESFIJAS != 'No aplica':
            print(Separadores._separadorComun)
            self._monto_multa = GetConsoleInput_Multa_Unidades_Fijas('Monto de la multa (en Unidades Fijas): ')
            if self._monto_multa == 'NULL':
                print(' - No se ingresó monto de multa.')
            else:
                print(f' - El monto ingresado es de {self._monto_multa} Unidades Fijas.')

        # Reincidencia
        if self._regimen_normativo.LIBERTAD_CONDICIONAL(LC_KEYS._ask_esReincidente_KEY):
            print(Separadores._separadorComun)
            while True:
                user_input = input('¿Hay declaración de reincidencia? (S/N): ')
                if user_input == "N" or user_input == "n" or user_input == '':
                    self._monto_de_pena.reincidencia = False
                    print(' - No es reincidente.')
                    break
                if user_input == "S" or user_input == "s":
                    self._monto_de_pena.reincidencia = True
                    print(' - Es reincidente.')
                    break
                print('ERROR: Solo se puede responder con "s" o "n"')

        # Si es pena temporal, pregunta si se aplicó la reclusión por tiempo indeterminado
        if self._ARGUMENTOS_pena_perpetua == False:
            if self._regimen_normativo.SALIDAS_TRANSITORIAS(ST_KEYS._ask_siHayAccesoria52) or self._regimen_normativo.LIBERTAD_CONDICIONAL(LC_KEYS._ask_siHayAccesoria52):
                print(Separadores._separadorComun)
                while True:
                    user_input = input('¿Se aplicó la accesoria del 52 CP (reclusión por tiempo indeterminado)? (S/N): ')
                    if user_input == "N" or user_input == "n" or user_input == '':
                        self._monto_de_pena.reclusionPorTiempoIndeterminado = False
                        print(' - No hay accesoria de reclusión por tiempo indeterminado.')
                        break
                    if user_input == "S" or user_input == "s":
                        self._monto_de_pena.reclusionPorTiempoIndeterminado = True
                        print(' - Se aplicó la accesoria de reclusión por tiempo indeterminado.')
                        break
                    print('ERROR: Solo se puede responder con "s" o "n"')

        # Otros tiempos de detención a computar
        print(Separadores._separadorComun)
        self._otras_detenciones = GetConsoleInput_OtrosTiemposDeDetencion()
        if self._otras_detenciones == 'NULL':
            print(' - No se ingresaron otras detenciones a computar.')
        else:
            print(' - Se van a computar las siguientes detenciones:')
            for otra_det in self._otras_detenciones:
                print(f'    - "{otra_det._nombre}": {otra_det._tiempoDeDetencion.años} año(s), {otra_det._tiempoDeDetencion.meses} mes(es) y {otra_det._tiempoDeDetencion.dias} día(s).')

        # Estímulo educativo
        print(Separadores._separadorComun)
        self._estimulo_educativo = GetConsoleInput_EstimuloEducativo()
        if self._estimulo_educativo.años == 0 and self._estimulo_educativo.meses == 0 and self._estimulo_educativo.dias == 0:
            print(' - No se aplica el estímulo educativo.')
        else:
            print(f' - El estímulo educativo es de {self._estimulo_educativo.años} año(s), {self._estimulo_educativo.meses} mes(es) y {self._estimulo_educativo.dias} día(s).')

        # PREGUNTAR SI ES POR DELITOS EXCLUIDOS LEY 25.892 o 25.948, según corresponda (son los mismos delitos)
        if (self._regimen_normativo.LIBERTAD_CONDICIONAL(LC_KEYS._ask_delitosExcluidos25892_KEY)
            or self._regimen_normativo.SALIDAS_TRANSITORIAS(ST_KEYS._ask_delitosExcluidos25948_KEY)
            or self._regimen_normativo.LIBERTAD_ASISTIDA(LA_KEYS._ask_delitosExcluidos25948_KEY)):
            print(Separadores._separadorComun)
            while True:
                print(self._regimen_normativo.DELITOS_EXCLUIDOS_LIBERTAD(DELITOS_EXCLUIDOS_LIB_KEYS._Ley_25892_y_25948_KEY))
                user_input = input('¿La condena es por alguno de los siguientes delitos? (S/N): ')
                if user_input == "N" or user_input == "n" or user_input == '':
                    if self._regimen_normativo.LIBERTAD_CONDICIONAL(LC_KEYS._ask_delitosExcluidos25892_KEY):
                        self._monto_de_pena.delitosExcluidosLey25892 = False
                    if(self._regimen_normativo.SALIDAS_TRANSITORIAS(ST_KEYS._ask_delitosExcluidos25948_KEY)
                       or self._regimen_normativo.LIBERTAD_ASISTIDA(LA_KEYS._ask_delitosExcluidos25948_KEY)):
                        self._monto_de_pena.delitosExcluidosLey25948 = False
                    print(' - No')
                    break
                if user_input == "S" or user_input == "s":
                    if self._regimen_normativo.LIBERTAD_CONDICIONAL(LC_KEYS._ask_delitosExcluidos25892_KEY):
                        self._monto_de_pena.delitosExcluidosLey25892 = True
                    if(self._regimen_normativo.SALIDAS_TRANSITORIAS(ST_KEYS._ask_delitosExcluidos25948_KEY)
                       or self._regimen_normativo.LIBERTAD_ASISTIDA(LA_KEYS._ask_delitosExcluidos25948_KEY)):
                        self._monto_de_pena.delitosExcluidosLey25948 = True
                    print(' - Si')
                    break
                print('ERROR: Solo se puede responder con "s" o "n"')

        # PREGUNTAR SI ES POR DELITOS EXCLUIDOS LEY 27.375
        if self._regimen_normativo.LIBERTAD_CONDICIONAL(LC_KEYS._ask_delitosExcluidos27375_KEY) or self._regimen_normativo.SALIDAS_TRANSITORIAS(ST_KEYS._ask_delitosExcluidos27375_KEY) or self._regimen_normativo.LIBERTAD_ASISTIDA(LA_KEYS._ask_delitosExcluidos27375_KEY) or self._regimen_normativo.REGIMEN_PREPARACION_LIBERTAD(REGPREPLIB_KEYS._ask_delitosExcluidos27375_KEY):
            print(Separadores._separadorComun)
            while True:
                print(self._regimen_normativo.DELITOS_EXCLUIDOS_LIBERTAD(DELITOS_EXCLUIDOS_LIB_KEYS._Ley_27375_KEY))
                user_input = input('¿La condena es por alguno de los delitos enumerados en la ley 27.375? (S/N): ')
                if user_input == "N" or user_input == "n" or user_input == '':
                    self._monto_de_pena.delitosExcluidosLey27375 = False
                    print(' - No')
                    break
                if user_input == "S" or user_input == "s":
                    self._monto_de_pena.delitosExcluidosLey27375 = True
                    print(' - Si')
                    break
                print('ERROR: Solo se puede responder con "s" o "n"')

        # PREGUNTA FECHA DE INICIO DE EJECUCIÓN DE PENA
        if self._regimen_normativo._regimen_LC == LC_REGIMENES._Ley_27375.value or self._regimen_normativo._regimen_ST == ST_REGIMENES._Ley_27375.value:
            print(Separadores._separadorComun)
            self._fecha_inicio_ejecucion = GetConsoleInput_Fecha('Fecha de inicio de ejecución o de ingreso a REAV (Dejar en blanco si aún no ejecuta pena): ', ENTER_devuelve_NULL=True)
            if self._fecha_inicio_ejecucion == 'NULL':
                print(' - No se ingresó fecha de inicio de ejecución. No se utilizará esta variable en el cómputo.')
                self._esta_ejecutando_pena = False
            else:
                print(f' - Fecha de inicio de ejecución: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_inicio_ejecucion)}')
                self._esta_ejecutando_pena = True

        # Si está ejecutando pena, hace las otras preguntas
        if self._esta_ejecutando_pena == True:
            # PREGUNTAR SI ESTA EN PERIODO DE PRUEBA, Y DESDE CUÁNDO
            if self._regimen_normativo.SALIDAS_TRANSITORIAS(ST_KEYS._ask_siEstaPeriodoDePruebaYDesdeCuando):
                print(Separadores._separadorComun)
                self._fecha_ingreso_periodo_de_prueba = GetConsoleInput_Fecha('Fecha de ingreso al periodo de prueba (Dejar en blanco si aún no ingresó al periodo de prueba): ', ENTER_devuelve_NULL=True)
                if self._fecha_ingreso_periodo_de_prueba == 'NULL':
                    print(' - No se ingresó fecha de ingreso al periodo de prueba. No se utilizará esta variable en el cómputo.')
                else:
                    print(f' - Fecha de ingreso al periodo de prueba: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_ingreso_periodo_de_prueba)}')

            # PREGUNTAR SI TIENE CONCEPTO BUENO DURANTE 2/3 DE LA EJECUCIÓN
            if self._regimen_normativo.LIBERTAD_CONDICIONAL(LC_KEYS._ask_2_3ConCalifBUENO_KEY):
                print(Separadores._separadorComun)
                self._fecha_calificacion_BUENO = GetConsoleInput_Fecha('Fecha en la que se obtuvo calificación "BUENO" (Dejar en blanco si aún no se obtuvo): ', ENTER_devuelve_NULL=True)
                if self._fecha_calificacion_BUENO == 'NULL':
                    print(' - No se ingresó fecha en la que se obtuvo calificación "BUENO". No se utilizará esta variable en el cómputo.')
                else:
                    print(f' - Fecha en la que se obtuvo calificación "BUENO": {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_calificacion_BUENO)}')

            # PREGUNTAR POR REQUISITO DE CALIFICACIÓN PARA ST
            if self._regimen_normativo.SALIDAS_TRANSITORIAS(ST_KEYS._ask_requisitoDeCalificacion):
                print(Separadores._separadorComun)
                self._fecha_calificacion_EJEMPLAR = GetConsoleInput_Fecha('Fecha en la que se obtuvo calificación "EJEMPLAR" (Dejar en blanco si aún no se obtuvo): ', ENTER_devuelve_NULL=True)
                if self._fecha_calificacion_EJEMPLAR == 'NULL':
                    print(' - No se ingresó fecha en la que se obtuvo calificación "EJEMPLAR". No se utilizará esta variable en el cómputo.')
                else:
                    print(f' - Fecha en la que se obtuvo calificación "EJEMPLAR": {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_calificacion_EJEMPLAR)}')

            # PREGUNTAR POR CRITERIO PARA APLICAR ESTÍMULO EDUCATIVO EN LAS SALIDAS TRANSITORIAS
            hay_estimulo_educativo = self._estimulo_educativo.años + self._estimulo_educativo.meses + self._estimulo_educativo.dias
            if self._regimen_normativo._regimen_ST == ST_REGIMENES._Ley_27375.value and hay_estimulo_educativo > 0:
                print(Separadores._separadorComun)
                while True:
                    print('Indicar el criterio para aplicar el estímulo educativo a las salidas transitorias')
                    print(Separadores._separadorComun)
                    print('1 --> Se aplica a los plazos para acceder al periodo de prueba.')
                    print('2 --> Se aplica a los plazos para acceder al periodo de prueba, y al requisito temporal de las salidas transitorias.')
                    print(Separadores._separadorComun)
                    user_input = input('INDICAR OPCIÓN: ')
                    if user_input == "1" or user_input == '':
                        self._vuelve_a_restar_otras_detenciones_y_140_en_ST = False
                        print(' - Se aplica la opción 1.')
                        break
                    if user_input == "2":
                        self._vuelve_a_restar_otras_detenciones_y_140_en_ST = True
                        print(' - Se aplica la opción 2.')
                        break
                    print('ERROR: Solo se puede responder con números 1 ó 2.')

class Preguntas_Input_Pena_EjecucionCondicional():
    def __init__(self):

        self._fecha_de_sentencia = 'NULL'
        self._fecha_firmeza_de_sentencia = 'NULL'
        self._monto_de_pena = 'NULL'

        self.GetConsoleInput_HacerPreguntas()

    def GetConsoleInput_HacerPreguntas(self):

        # Fecha de la sentencia
        print(Separadores._separadorComun)
        self._fecha_de_sentencia = GetConsoleInput_Fecha('Ingresar la fecha de la sentencia: ')
        print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_de_sentencia)}')

        # Fecha de firmeza de la sentencia
        print(Separadores._separadorComun)
        self._fecha_firmeza_de_sentencia = GetConsoleInput_Fecha('Ingresar la fecha en la que la sentencia adquirió firmeza: ')
        print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_firmeza_de_sentencia)}')

        # Monto de pena
        print(Separadores._separadorComun)
        self._monto_de_pena = GetConsoleInput_MontoDePena_temporal(ejecucionCondicional=True)
        print(f' - La pena ingresada es de {self._monto_de_pena.años} año(s), {self._monto_de_pena.meses} mes(es), {self._monto_de_pena.dias} día(s).')
        print(f' - El plazo de control es de {self._monto_de_pena.plazoControl_años} año(s), {self._monto_de_pena.plazoControl_meses} mes(es), {self._monto_de_pena.plazoControl_dias} día(s).')

class Preguntas_Input_NuevoComputoPorLibertadRevocada():
    def __init__(self):

        self._fecha_del_hecho = 'NULL'
        self._fecha_de_egreso = 'NULL'
        self._fecha_nueva_detencion = 'NULL'
        self._fecha_libertad_condicional = 'NULL'
        self._fecha_salidas_transitorias = 'NULL'
        self._vencimiento_de_pena = 'NULL'
        self._libertad_evadida = 'NULL'
        self._computa_tiempo_en_libertad = 'NULL'

        self.GetConsoleInput_HacerPreguntas()

    def GetConsoleInput_HacerPreguntas(self):

        # Tipo de libertad
        print(Separadores._separadorComun)
        while True:
            print('Indicar la circunstancia en la que se revocó la libertad:')
            print(Separadores._separadorComun)
            print('1 --> Se revocó la libertad condicional.')
            print('2 --> Se revocó la libertad asistida.')
            print('3 --> No retornó de una salida transitoria.')
            print('4 --> Fuga del establecimiento penitenciario.')
            print(Separadores._separadorComun)
            user_input = input('INDICAR OPCIÓN: ')
            if user_input == "1":
                self._libertad_evadida = LIBERTAD_EVADIDA._libertad_condicional.value
                print(' - Se aplica la opción 1.')
                break
            if user_input == "2":
                self._libertad_evadida = LIBERTAD_EVADIDA._libertad_asistida.value
                print(' - Se aplica la opción 2.')
                break
            if user_input == "3":
                self._libertad_evadida = LIBERTAD_EVADIDA._salidas_transitorias.value
                print(' - Se aplica la opción 3.')
                break
            if user_input == "4":
                self._libertad_evadida = LIBERTAD_EVADIDA._fuga.value
                print(' - Se aplica la opción 4.')
                break
            print('ERROR: Solo se puede responder con números 1 a 4.')

        # Fecha del hecho
        print(Separadores._separadorComun)
        self._fecha_del_hecho = GetConsoleInput_Fecha('Ingresar la fecha del hecho: ')
        print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_del_hecho)}')

        # Fecha de egreso en libertad
        print(Separadores._separadorComun)
        self._fecha_de_egreso = GetConsoleInput_Fecha('Ingresar la fecha de egreso en libertad: ')
        print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_de_egreso)}')

        # Fecha de la nueva detención
        print(Separadores._separadorComun)
        self._fecha_nueva_detencion = GetConsoleInput_Fecha('Ingresar la fecha de la nueva detención: ')
        print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_nueva_detencion)}')

        # Fecha del vencimiento de pena
        print(Separadores._separadorComun)
        self._vencimiento_de_pena = GetConsoleInput_Fecha('Ingresar la fecha en la que correspondía el vencimiento de pena: ')
        print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._vencimiento_de_pena)}')

        # Pregunta si computa el tiempo en libertad (para casos de libertad condicional o asistida)
        if (self._libertad_evadida == LIBERTAD_EVADIDA._libertad_condicional.value
        or self._libertad_evadida == LIBERTAD_EVADIDA._libertad_asistida.value):
            print(Separadores._separadorComun)
            while True:
                user_input = input('¿Se computa el tiempo que duró la libertad? (S/N): ')
                if user_input == "N" or user_input == "n" or user_input == '':
                    self._computa_tiempo_en_libertad = False
                    print(' - No se computa el tiempo que duró la libertad.')
                    break
                if user_input == "S" or user_input == "s":
                    self._computa_tiempo_en_libertad = True
                    print(' - Se computa el tiempo que duró la libertad.')
                    break
                print('ERROR: Solo se puede responder con "s" o "n"')

        # Fecha de libertad condicional
        if (self._libertad_evadida == LIBERTAD_EVADIDA._fuga.value
        or self._libertad_evadida == LIBERTAD_EVADIDA._salidas_transitorias.value):
            print(Separadores._separadorComun)
            self._fecha_libertad_condicional = GetConsoleInput_Fecha('Ingresar la fecha en la que correspondía la libertad condicional (dejar en blanco si no se cuenta con esa fecha): ', ENTER_devuelve_NULL=True)
            if self._fecha_libertad_condicional == 'NULL':
                print(' - No se ingresó fecha.')
            else:
                print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_libertad_condicional)}')

        # Fecha de salidas transitorias
        if self._libertad_evadida == LIBERTAD_EVADIDA._fuga.value:
            print(Separadores._separadorComun)
            self._fecha_salidas_transitorias = GetConsoleInput_Fecha('Ingresar la fecha en la que correspondían las salidas transitorias (dejar en blanco si no se cuenta con esa fecha): ', ENTER_devuelve_NULL=True)
            if self._fecha_salidas_transitorias == 'NULL':
                print(' - No se ingresó fecha.')
            else:
                print(f' - Fecha ingresada: {Datetime_date_enFormatoXX_XX_XXXX(self._fecha_salidas_transitorias)}')

class Separadores():
    _separadorComun = ''

def Datetime_date_enFormatoXX_XX_XXXX(_date:datetime.date):
    if type(_date) is not datetime.date:
        return _date

    toRT = str(_date)
    return '{}/{}/{}'.format(toRT[8:10], toRT[5:7], toRT[0:4])

def NumeroConSeparadorDeMiles(numero:Union[int, float]):

    if type(numero) is int:
        return f"{numero:,}".replace(',', '.')

    if type(numero) is float:
        numero = f'{numero:,.2f}'.replace(',','h')
        numero = f'{numero}'.replace('.',',')
        numero = f'{numero}'.replace('h','.')
        return numero

    print('def NumeroConSeparadorDeMiles: WARNING: El valor ingresao no es ni float ni int. La función no va a hacer nada.')

def FechaA_es_Mayor_Que_FechaB(fecha_a:datetime.date, fecha_b:datetime.date):
    temp = fecha_a - fecha_b
    if temp.days > 0:
        return True
    else:
        return False

def FechaA_es_Mayor_O_Igual_Que_FechaB(fecha_a:datetime.date, fecha_b:datetime.date):
    temp = fecha_a - fecha_b
    if temp.days >= 0:
        return True
    else:
        return False

def FechaA_es_Igual_Que_FechaB(fecha_a:datetime.date, fecha_b:datetime.date):
    temp = fecha_a - fecha_b
    if temp.days == 0:
        return True
    else:
        return False

def Comparar_fechas_y_devolver_la_mayor(*fechas):
    TRET = True
    for fecha in fechas:
        if type(fecha) is datetime.date:
            TRET = fecha
            break
    if TRET == True:
        print('Comparar_fechas_y_devolver_la_mayor: NINGUNA DE LOS PARÁMETROS INGRESADOS ES UNA FECHA. NO SE REALIZÓ LA COMPARACIÓN.')
        return

    for fecha in fechas:
        if type(fecha) is datetime.date:
            if FechaA_es_Mayor_Que_FechaB(fecha, TRET):
                TRET = fecha
    return TRET

def MontoDeTiempoA_es_Mayor_que_MontoDeTiempoB(tiempo_a:TiempoEn_Años_Meses_Dias, tiempo_b:TiempoEn_Años_Meses_Dias):
    if tiempo_a.años > tiempo_b.años:
        return True
    if tiempo_a.años < tiempo_b.años:
        return False
    if tiempo_a.meses > tiempo_b.meses:
        return True
    if tiempo_a.meses < tiempo_b.meses:
        return False
    if tiempo_a.dias > tiempo_b.dias:
        return True
    else:
        return False

def es_multiplo(numero, multiplo):
    return numero % multiplo == 0

def GetConsoleInput_Fecha(mensaje_para_el_usuario='Fecha: ', ENTER_devuelve_NULL=False, forceInput:str='') -> datetime.date:
    '''Hace ingresar por consola una fecha (formatos XX/XX/XXXX ó X/X/XX) y la devuelve como un datetime.date\n
    ENTER_devuelve_NULL=True: Si no se ingresa una fecha válida, devuelve "NULL"\n
    ENTER_devuelve_NULL=False: Si se ingresa una fecha inválida, la vuelve a solicitar\n
    forceInput="": Si se ingresa un string, la función no solicita input, y utiliza ese string como si se hubiera ingresado por consola'''
    while True:
        seImprimioError=False
        try:
            if forceInput == '':
                user_input = input(mensaje_para_el_usuario)
            else:
                user_input = forceInput
            if len(user_input) < 6 or len(user_input) > 10:
                if ENTER_devuelve_NULL:
                    return 'NULL'
                else:
                    print('ERROR: No se ingresó una fecha válida')
                    seImprimioError=True
                    raise Exception

            cursor = 0
            number_start = 0
            number_end = 0
            fecha = []

            for x in range(2):
                for y in range(3):
                    if user_input[cursor] == "/":
                        number_end = cursor - 1
                        break
                    else:
                        cursor += 1
                if number_start == number_end:
                    fecha.append(int(user_input[number_start]))
                else:
                    fecha.append(int(user_input[number_start:number_end+1]))

                cursor += 1
                number_start = cursor

            # Para el año, busca desde el fondo
            number_end = cursor = len(user_input) - 1
            for y in range(5):
                if user_input[cursor] == "/":
                    number_start = cursor + 1
                    break
                else:
                    cursor -= 1

            # Revisa que los años tengan 2 dígitos o 4
            year_digits = len(user_input[number_start:number_end+1])
            if year_digits != 2 and year_digits != 4:
                if ENTER_devuelve_NULL:
                    return 'NULL'
                else:
                    print('ERROR: Solo se aceptan los años en formato de dos o cuatro dígitos.')
                    seImprimioError=True
                    raise Exception

            year = int(user_input[number_start:number_end+1])

            # Si el año ingresado es de dos dígitos, lo pasa a formato de 4 dígitos
            if year_digits == 2:
                if year <= 60:
                    year += 2000
                else:
                    year += 1900

            fecha.append(year)
            return datetime.date(fecha[2], fecha[1], fecha[0])
        except:
            if ENTER_devuelve_NULL:
                return 'NULL'
            else:
                if seImprimioError == False:
                    print('ERROR: No se ingresó una fecha válida')

def GetConsoleInput_MontoDePena_temporal(ejecucionCondicional:bool=False):
    '''Hace ingresar por consola un monto de pena temporal y la devuelve como un MontoDePena()\n
    Por el momento solo pide el monto de tiempo. No consulta por reincidencia, accesoria del 52 CP, etc.'''
    montoDePena = MontoDePena()

    # Intenta tomar monto de pena. Si se ingresa 0, 0, 0, vuelve a intentar
    while True:
        # Ingresar años
        while True:
            try:
                _years = input('Ingresar monto de pena (años): ')
                if _years == '':
                    _years = 0
                    break
                _years = int(_years)
                if _years >= 0 and _years <= 50:
                    break
                print(' - ERROR: El monto de pena en años debe ser un número entero entre 0 y 50.')
            except:
                print(' - ERROR: El monto de pena en años debe ser un número entero entre 0 y 50.')

        # Ingresar meses
        while True:
            try:
                _months = input('Ingresar monto de pena (meses): ')
                if _months == '':
                    _months = 0
                    break
                _months = int(_months)
                if _months >= 0 and _months <= 11:
                    break
                print(' - ERROR: El monto de pena en meses debe ser un número entero entre 0 y 11.')
            except:
                print(' - ERROR: El monto de pena en meses debe ser un número entero entre 0 y 11.')

        # Ingresar días
        while True:
            try:
                _days = input('Ingresar monto de pena (días): ')
                if _days == '':
                    _days = 0
                    break
                _days = int(_days)
                if _days >= 0 and _days <= 30:
                    break
                print(' - ERROR: El monto de pena en días debe ser un número entero entre 0 y 30.')
            except:
                print(' - ERROR: El monto de pena en días debe ser un número entero entre 0 y 30.')

        _total = _years + _months + _days
        if _total != 0:
            break
        print(' - ERROR: No se ingresó ninguna pena. Intente de nuevo.')

    if ejecucionCondicional:
        _plazoControl_years = 0
        _PlazoControl_months = 0
        _PlazoControl_days = 0
        print(Separadores._separadorComun)
        # Intenta tomar el plazo de control. Si se ingresa 0, 0, 0, vuelve a intentar

        # Ingresar años
        while True:
            try:
                _plazoControl_years = input('Ingresar plazo de control (años): ')
                if _plazoControl_years == '':
                    _plazoControl_years = 2
                    break
                _plazoControl_years = int(_plazoControl_years)
                if _plazoControl_years >= 2 and _plazoControl_years <= 4:
                    break
                print(' - ERROR: Solo se puede ingresar un número entero entre 2 y 4.')
            except:
                print(' - ERROR: Solo se puede ingresar un número entero entre 2 y 4.')

        if _plazoControl_years != 4:
            # Ingresar meses
            while True:
                try:
                    _PlazoControl_months = input('Ingresar plazo de control (meses): ')
                    if _PlazoControl_months == '':
                        _PlazoControl_months = 0
                        break
                    _PlazoControl_months = int(_PlazoControl_months)
                    if _PlazoControl_months >= 0 and _PlazoControl_months <= 11:
                        break
                    print(' - ERROR: Solo se puede ingresar un número entero entre 0 y 11.')
                except:
                    print(' - ERROR: Solo se puede ingresar un número entero entre 0 y 11.')

            # Ingresar días
            while True:
                try:
                    _PlazoControl_days = input('Ingresar monto de pena (días): ')
                    if _PlazoControl_days == '':
                        _PlazoControl_days = 0
                        break
                    _PlazoControl_days = int(_PlazoControl_days)
                    if _PlazoControl_days >= 0 and _PlazoControl_days <= 30:
                        break
                    print(' - ERROR: Solo se puede ingresar un número entero entre 0 y 30.')
                except:
                    print(' - ERROR: Solo se puede ingresar un número entero entre 0 y 30.')

        montoDePena.plazoControl_años = _plazoControl_years
        montoDePena.plazoControl_meses = _PlazoControl_months
        montoDePena.plazoControl_dias = _PlazoControl_days
        montoDePena.ejecucionCondicional = True

    # Arma lo que va a devolver
    montoDePena.años = _years
    montoDePena.meses = _months
    montoDePena.dias = _days

    return montoDePena

def GetConsoleInput_Multa_Unidades_Fijas(mensaje_para_el_usuario:str='Ingresar monto de la multa (en unidades fijas): ', _min:float=45, _max:float=900) -> Union[int,float]:
    '''_min=45 : Es el monto mínimo de multa que permite la función. Por defecto es 45.\n
       _max=900 : Es el monto máximo de multa que permite la función. Por defecto es 900.\n
       Si el mínimo ingresado es mayor al máximo, la función invierte los valores para que siempre el máximo sea mayor
       o igual al mínimo.\n
       Si se ingresa 0, o nada, devuelve str -> "NULL"'''

    if _min > _max:
        _newMax = _min
        _newMin = _max
        _min = _newMin
        _max = _newMax

    while True:
        try:
            _multa = input(mensaje_para_el_usuario)
            if _multa == '' or _multa == 0:
                _multa = 'NULL'
                break
            _multa = int(_multa)
            if _multa >= _min and _multa <= _max:
                break
            print(f' - ERROR: El monto de la multa debe ser un número entre {_min} y {_max}.')
        except:
            print(f' - ERROR: El monto de la multa debe ser un número entre {_min} y {_max}.')
    return _multa

def GetConsoleInput_OtrosTiemposDeDetencion() -> list[OtraDetencion]:
    OTDD = []
    seguir_preguntando = True
    init_query = input('Ingresar otros tiempos de detención a computar? (S/N): ')
    if init_query == "S" or init_query == "s":
        while seguir_preguntando:
            f_det = ''
            f_lib = ''
            f_nombre = input('Ingresar denominación o referencia para esta detención: ')
            if f_nombre == '':
                f_nombre = 'Sin nombre'
            f_det = GetConsoleInput_Fecha('Ingresar fecha de detención del otro tiempo a computar: ')
            f_lib = GetConsoleInput_Fecha('Ingresar fecha de libertad: ')
            este = OtraDetencion(f_det, f_lib, nombre=f_nombre)
            OTDD.append(este)
            seguir = input('Necesita ingresar otro tiempo de detención? (S/N): ')
            if seguir == "S" or seguir == "s":
                seguir_preguntando = True
            else:
                seguir_preguntando = False
        return OTDD
    else:
        return "NULL"

def GetConsoleInput_EstimuloEducativo() -> TiempoEn_Años_Meses_Dias:

    estimulo = TiempoEn_Años_Meses_Dias()

    # Ingresar años
    while True:
        try:
            _years = input('Ingresar estímulo educativo (años): ')
            if _years == '':
                _years = 0
                break
            _years = int(_years)
            if _years >= 0 and _years <= 50:
                break
            print(' - ERROR: El monto en años debe ser un número entre 0 y 50.')
        except:
            print(' - ERROR: El monto en años debe ser un número entre 0 y 50.')

    # Ingresar meses
    while True:
        try:
            _months = input('Ingresar estímulo educativo (meses): ')
            if _months == '':
                _months = 0
                break
            _months = int(_months)
            if _months >= 0 and _months <= 11:
                break
            print(' - ERROR: El monto meses debe ser un número entre 0 y 11.')
        except:
            print(' - ERROR: El monto en meses debe ser un número entre 0 y 11.')

    # Ingresar días
    while True:
        try:
            _days = input('Ingresar estímulo educativo (días): ')
            if _days == '':
                _days = 0
                break
            _days = int(_days)
            if _days >= 0 and _days <= 30:
                break
            print(' - ERROR: El monto en días debe ser un número entre 0 y 30.')
        except:
            print(' - ERROR: El monto en días debe ser un número entre 0 y 30.')

    estimulo.años = _years
    estimulo.meses = _months
    estimulo.dias = _days

    return estimulo

def RestarOtrasDetenciones(fecha_de_requisito_temporal:datetime.date, otras_detenciones:list[OtraDetencion]):
        toreturn = fecha_de_requisito_temporal
        for otra_det in otras_detenciones:
            toreturn += relativedelta(days=-otra_det._tiempoDeDetencion.dias)
            toreturn += relativedelta(months=-otra_det._tiempoDeDetencion.meses)
            toreturn += relativedelta(years=-otra_det._tiempoDeDetencion.años)
        return toreturn

if __name__ == '__main__':
    fechadet = datetime.date(2014, 6, 15)
    fechalib = datetime.date(2014, 7, 16)
    x = OtraDetencion(fechadet, fechalib)


import torch
import torch.nn as nn
import json

from processor import PreTrainData

class NextSentencePrediction(nn.Module):
    def __init__(self, hidden_size):
        super(NextSentencePrediction, self).__init__()
        self.pooler = nn.Sequential(
            nn.Linear(hidden_size, hidden_size),
            nn.Tanh())
        self.fc = nn.Linear(hidden_size, 2)

    def forward(self, mini_batch_ref):
        sequence_output = mini_batch_ref['sequence_output']

        first_token_output = self.pooler(sequence_output[:, 0, :])
        nsp_output = self.fc(first_token_output)

        return nsp_output

class SentenceOrderPrediction(nn.Module):
    def __init__(self, hidden_size):
        super(SentenceOrderPrediction, self).__init__()
        self.pooler = nn.Sequential(
            nn.Linear(hidden_size, hidden_size),
            nn.Tanh())
        self.fc = nn.Linear(hidden_size, 2)

    def forward(self, mini_batch_ref):
        sequence_output = mini_batch_ref['sequence_output']

        first_token_output = self.pooler(sequence_output[:, 0, :])
        sop_output = self.fc(first_token_output)

        return sop_output

class MaskedLanguageModel(nn.Module):
    def __init__(self, output_size, hidden_size):
        super(MaskedLanguageModel, self).__init__()

        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, mini_batch_ref):
        sequence_output = mini_batch_ref['sequence_output']
        mlm_positions = mini_batch_ref['mlm_positions']

        # select the to-predict tokens from sequence output
        batch_size, max_seq_length, hidden_size = sequence_output.shape
        sequence_output = sequence_output.view(-1, hidden_size)
        mlm_positions = (mlm_positions + (torch.arange(0, batch_size) * max_seq_length).view(-1, 1)).view(-1)
        mlm_tokens_output = sequence_output.index_select(0, mlm_positions)

        # [batch_size * max_predictions_per_seq, hidden_size]
        mlm_output = self.fc(mlm_tokens_output)

        return mlm_output

class SpanBoundaryObjective(nn.Module):
    def __init__(self, output_size, hidden_size):
        super(SpanBoundaryObjective, self).__init__()

        self.fc = nn.Linear(hidden_size * 3, output_size)

    def forward(self, sequence_output, sbo_positions):
        batch_size, max_seq_length, hidden_size = sequence_output.shape
        sequence_output = sequence_output.view(-1, 1)
        sbo_positions = (sbo_positions + (torch.arange(0, batch_size) * max_seq_length).view(-1, 1)).view(-1)
        left_boundary = sequence_output.index_select(0, sbo_positions)[::2]
        right_boundary = sequence_output.index_select(0, sbo_positions)[1::2]

        return

class BertPreTrain(nn.Module):
    def __init__(self, bert_model):
        super(BertPreTrain, self).__init__()

        self.bert = bert_model
        self.mlm = MaskedLanguageModel(bert_model.config.vocab_size, bert_model.config.hidden_size)
        self.nsp = NextSentencePrediction(bert_model.config.hidden_size)

    def forward(self, mini_batch_ref):
        x = mini_batch_ref['input_ids']
        mask = mini_batch_ref['input_mask']
        segment_ids = mini_batch_ref['segment_ids']
        mini_batch_ref['sequence_output'] = self.bert(x, mask, segment_ids)

        prediction_scores = self.mlm(mini_batch_ref)
        next_sentence_scores = self.nsp(mini_batch_ref)

        return prediction_scores, next_sentence_scores

class PreTrainModel(nn.Module):
    def __init__(self, ptm_config_file, bert_model):
        super(PreTrainModel, self).__init__()
        self.transformer = bert_model
        self.pretrain_models = {
            'nsp': none(),
            'sop': none(),
            'mlm': none(),
            'sbo': none(),
            'tif': none()
        }
        self.process_stream = []

        self.get_process_stream(ptm_config_file)

    def get_process_stream(self, ptm_config_file):
        with open(ptm_config_file, 'r') as reader:
            config_dict = json.load(reader)
            for key, value in config_dict.items():
                self.__dict__[key] = value

        # For sentence prediction models
        if self.nsp:
            self.process_stream.append(create_nsp)
            self.pretrain_models['nsp'] = \
                NextSentencePrediction(self.transformer.config.hidden_size)
        elif self.sop:
            self.process_stream.append(create_sop)
            self.pretrain_models['sop'] = \
                SentenceOrderPrediction(self.transformer.config.hidden_size)
        else:
            self.process_stream.append(full_document_sampling)
        # For masked language models
        if not self.sbo:
            self.process_stream.append(create_mlm)
            self.pretrain_models['mlm'] =\
                MaskedLanguageModel(self.transformer.config.vocab_size, self.transformer.config.hidden_size)
        else:
            self.process_stream.append(create_sbo)
            self.pretrain_models['sbo'] =\
                SpanBoundaryObjective(self.transformer.config.vocab_size, self.transformer.config.hidden_size)
        # self.process_stream['tif'] = create_tif

    def create_instances(self, input_file, tokenizer):
        pt_data = PreTrainData(input_file, tokenizer)
        for process in self.process_stream:
            if process:
                process(pt_data, self.transformer.config)

        return pt_data.get_iterator(self.transformer.config)

    def forward(self, mini_batch_ref):
        # Transformer model
        x = mini_batch_ref['input_ids']
        mask = mini_batch_ref['input_mask']
        segment_ids = mini_batch_ref['segment_ids']
        mini_batch_ref['sequence_output'] = self.transformer(x, mask, segment_ids)

        ptm_outputs = {
            'sp': None,
            'mlm': None,
            'sbo': None,
            'tif': None
        }
        for ptm, ptm_func in self.pretrain_models.items():
            if ptm_func:
                ptm_outputs[ptm] = ptm_func(mini_batch_ref)

        return ptm_outputs

def create_nsp(ptd, config):
    return ptd.create_nsp(config)

def create_sop(ptd, config):
    return ptd.create_sop(config)

def full_document_sampling(ptd, config):
    return ptd.full_document_sampling(config)

def create_mlm(ptd, config):
    return ptd.create_mlm(config)

def create_sbo(ptd, config):
    return ptd.create_sbo(config)

def none():
    pass

from albert import AlbertConfig, Albert
import tokenization

bert_config = AlbertConfig.from_json('bert_config.json')
transformer = Albert(bert_config)
bertx = PreTrainModel('ptm_config.json', transformer)
train_iterator = bertx.create_instances('00.txt', tokenization.ChineseWordpieceTokenizer('vocab.txt'))
for batch in train_iterator:
    bertx(batch)