<commit_before>#! -*- coding: utf8 -*-

import unittest
from singing_girl import sing
from decimal import Decimal

class TestTraductorNumeros(unittest.TestCase):

    def assertSing(self, number, text):
        self.assertEquals(sing(number), text)

    def test_digitos(self):
        self.assertSing(0, 'cero')
        self.assertSing(1, 'uno')
        self.assertSing(2, 'dos')
        self.assertSing(3, 'tres')
        self.assertSing(4, 'cuatro')
        self.assertSing(5, 'cinco')
        self.assertSing(6, 'seis')
        self.assertSing(7, 'siete')
        self.assertSing(8, 'ocho')
        self.assertSing(9, 'nueve')

    def test_decenas(self):
        self.assertSing(10, 'diez')
        self.assertSing(11, 'once')
        self.assertSing(12, 'doce')
        self.assertSing(13, 'trece')
        self.assertSing(14, 'catorce')
        self.assertSing(15, 'quince')
        self.assertSing(16, 'dieciseis')
        self.assertSing(17, 'diecisiete')
        self.assertSing(18, 'dieciocho')
        self.assertSing(19, 'diecinueve')
        self.assertSing(20, 'veinte')
        self.assertSing(21, 'veintiuno')
        self.assertSing(22, 'veintidós')
        self.assertSing(23, 'veintitrés')
        self.assertSing(24, 'veinticuatro')
        self.assertSing(25, 'veinticinco')
        self.assertSing(26, 'veintiséis')
        self.assertSing(27, 'veintisiete')
        self.assertSing(28, 'veintiocho')
        self.assertSing(29, 'veintinueve')
        self.assertSing(30, 'treinta')
        self.assertSing(37, 'treinta y siete')
        self.assertSing(40, 'cuarenta')
        self.assertSing(42, 'cuarenta y dos')
        self.assertSing(50, 'cincuenta')
        self.assertSing(55, 'cincuenta y cinco')
        self.assertSing(60, 'sesenta')
        self.assertSing(66, 'sesenta y seis')
        self.assertSing(70, 'setenta')
        self.assertSing(77, 'setenta y siete')
        self.assertSing(80, 'ochenta')
        self.assertSing(88, 'ochenta y ocho')
        self.assertSing(90, 'noventa')
        self.assertSing(99, 'noventa y nueve')

    def test_centenas(self):
        self.assertSing(100, 'cien')
        self.assertSing(111, 'ciento once')
        self.assertSing(200, 'doscientos')
        self.assertSing(222, 'doscientos veintidós')
        self.assertSing(300, 'trescientos')
        self.assertSing(333, 'trescientos treinta y tres')
        self.assertSing(400, 'cuatrocientos')
        self.assertSing(444, 'cuatrocientos cuarenta y cuatro')
        self.assertSing(500, 'quinientos')
        self.assertSing(555, 'quinientos cincuenta y cinco')
        self.assertSing(600, 'seiscientos')
        self.assertSing(666, 'seiscientos sesenta y seis')
        self.assertSing(700, 'setecientos')
        self.assertSing(777, 'setecientos setenta y siete')
        self.assertSing(800, 'ochocientos')
        self.assertSing(888, 'ochocientos ochenta y ocho')
        self.assertSing(900, 'novecientos')
        self.assertSing(953, 'novecientos cincuenta y tres')
        self.assertSing(999, 'novecientos noventa y nueve')

    def test_miles(self):
        self.assertSing(4326, 'cuatro mil trescientos veintiséis')
        self.assertSing(7532, 'siete mil quinientos treinta y dos')
        self.assertSing(1014, 'mil catorce')
        self.assertSing(21000, 'veintiun mil')
        self.assertSing(71000, 'setenta y un mil')

        self.assertSing(916543, 'novecientos dieciseis mil quinientos cuarenta y tres')

    def test_millones(self):
        self.assertSing(10 ** 6, 'un millon')
        self.assertSing(10 ** 12, 'un billon')
        self.assertSing(10 ** 18, 'un trillon')
        self.assertSing(10 ** 24, 'un cuatrillon')

    def test_numeros_grandes(self):
        self.assertSing(1000000, 'un millon')
        self.assertSing(1000021, 'un millon veintiuno')
        self.assertSing(41000021, 'cuarenta y un millones veintiuno')
        self.assertSing(41000021, 'cuarenta y un millones veintiuno')

        self.assertSing(416010015, 'cuatrocientos dieciseis millones diez mil quince')
        self.assertSing(123123123123123123123456123456,
                        'ciento veintitrés mil ciento veintitrés cuatrillones '
                        'ciento veintitrés mil ciento veintitrés trillones '
                        'ciento veintitrés mil ciento veintitrés billones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis millones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis')

    def test_decimales(self):
        self.assertSing(16.1, 'dieciseis con 10/100')
        self.assertSing(16.321, 'dieciseis con 32/100')
        self.assertSing(16.80, 'dieciseis con 80/100')
        self.assertSing(16.51, 'dieciseis con 51/100')
        self.assertSing(1.75, 'uno con 75/100')
        self.assertSing(Decimal('123123123123123123123456123456.33'),
                        'ciento veintitrés mil ciento veintitrés cuatrillones '
                        'ciento veintitrés mil ciento veintitrés trillones '
                        'ciento veintitrés mil ciento veintitrés billones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis millones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis con 33/100')
        self.assertSing(Decimal('123123123123123123123456123456.67'),
                        'ciento veintitrés mil ciento veintitrés cuatrillones '
                        'ciento veintitrés mil ciento veintitrés trillones '
                        'ciento veintitrés mil ciento veintitrés billones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis millones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis con 67/100')

    def test_value_error_raised_on_invalid_number(self):
        self.assertRaises(ValueError, sing, 10 ** 30)

if __name__ == '__main__':
    unittest.main()
<commit_msg>changing tests<commit_after>#! -*- coding: utf8 -*-

import unittest
from singing_girl import sing
from decimal import Decimal

class TestTraductorNumeros(unittest.TestCase):

    def assertSing(self, number, text):
        self.assertEquals(sing(number), text)

    def test_digitos(self):
        self.assertSing(0, 'cero')
        self.assertSing(1, 'uno')
        self.assertSing(2, 'dos')
        self.assertSing(3, 'tres')
        self.assertSing(4, 'cuatro')
        self.assertSing(5, 'cinco')
        self.assertSing(6, 'seis')
        self.assertSing(7, 'siete')
        self.assertSing(8, 'ocho')
        self.assertSing(9, 'nueve')

    def test_decenas(self):
        self.assertSing(10, 'diez')
        self.assertSing(11, 'once')
        self.assertSing(12, 'doce')
        self.assertSing(13, 'trece')
        self.assertSing(14, 'catorce')
        self.assertSing(15, 'quince')
        self.assertSing(16, 'dieciséis')
        self.assertSing(17, 'diecisiete')
        self.assertSing(18, 'dieciocho')
        self.assertSing(19, 'diecinueve')
        self.assertSing(20, 'veinte')
        self.assertSing(21, 'veintiuno')
        self.assertSing(22, 'veintidós')
        self.assertSing(23, 'veintitrés')
        self.assertSing(24, 'veinticuatro')
        self.assertSing(25, 'veinticinco')
        self.assertSing(26, 'veintiséis')
        self.assertSing(27, 'veintisiete')
        self.assertSing(28, 'veintiocho')
        self.assertSing(29, 'veintinueve')
        self.assertSing(30, 'treinta')
        self.assertSing(37, 'treinta y siete')
        self.assertSing(40, 'cuarenta')
        self.assertSing(42, 'cuarenta y dos')
        self.assertSing(50, 'cincuenta')
        self.assertSing(55, 'cincuenta y cinco')
        self.assertSing(60, 'sesenta')
        self.assertSing(66, 'sesenta y seis')
        self.assertSing(70, 'setenta')
        self.assertSing(77, 'setenta y siete')
        self.assertSing(80, 'ochenta')
        self.assertSing(88, 'ochenta y ocho')
        self.assertSing(90, 'noventa')
        self.assertSing(99, 'noventa y nueve')

    def test_centenas(self):
        self.assertSing(100, 'cien')
        self.assertSing(111, 'ciento once')
        self.assertSing(200, 'doscientos')
        self.assertSing(222, 'doscientos veintidós')
        self.assertSing(300, 'trescientos')
        self.assertSing(333, 'trescientos treinta y tres')
        self.assertSing(400, 'cuatrocientos')
        self.assertSing(444, 'cuatrocientos cuarenta y cuatro')
        self.assertSing(500, 'quinientos')
        self.assertSing(555, 'quinientos cincuenta y cinco')
        self.assertSing(600, 'seiscientos')
        self.assertSing(666, 'seiscientos sesenta y seis')
        self.assertSing(700, 'setecientos')
        self.assertSing(777, 'setecientos setenta y siete')
        self.assertSing(800, 'ochocientos')
        self.assertSing(888, 'ochocientos ochenta y ocho')
        self.assertSing(900, 'novecientos')
        self.assertSing(953, 'novecientos cincuenta y tres')
        self.assertSing(999, 'novecientos noventa y nueve')

    def test_miles(self):
        self.assertSing(4326, 'cuatro mil trescientos veintiséis')
        self.assertSing(7532, 'siete mil quinientos treinta y dos')
        self.assertSing(1014, 'mil catorce')
        self.assertSing(21000, 'veintiun mil')
        self.assertSing(71000, 'setenta y un mil')

        self.assertSing(916543, 'novecientos dieciséis mil quinientos cuarenta y tres')

    def test_millones(self):
        self.assertSing(10 ** 6, 'un millón')
        self.assertSing(10 ** 12, 'un billón')
        self.assertSing(10 ** 18, 'un trillón')
        self.assertSing(10 ** 24, 'un cuatrillón')

    def test_numeros_grandes(self):
        self.assertSing(1000000, 'un millón')
        self.assertSing(1000021, 'un millón veintiuno')
        self.assertSing(41000021, 'cuarenta y un millónes veintiuno')
        self.assertSing(41000021, 'cuarenta y un millónes veintiuno')

        self.assertSing(416010015, 'cuatrocientos dieciséis millones diez mil quince')
        self.assertSing(123123123123123123123456123456,
                        'ciento veintitrés mil ciento veintitrés cuatrillones '
                        'ciento veintitrés mil ciento veintitrés trillones '
                        'ciento veintitrés mil ciento veintitrés billones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis millones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis')

    def test_decimales(self):
        self.assertSing(16.1, 'dieciséis con 10/100')
        self.assertSing(16.321, 'dieciséis con 32/100')
        self.assertSing(16.80, 'dieciséis con 80/100')
        self.assertSing(16.51, 'dieciséis con 51/100')
        self.assertSing(1.75, 'uno con 75/100')
        self.assertSing(Decimal('123123123123123123123456123456.33'),
                        'ciento veintitrés mil ciento veintitrés cuatrillones '
                        'ciento veintitrés mil ciento veintitrés trillones '
                        'ciento veintitrés mil ciento veintitrés billones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis millones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis con 33/100')
        self.assertSing(Decimal('123123123123123123123456123456.67'),
                        'ciento veintitrés mil ciento veintitrés cuatrillones '
                        'ciento veintitrés mil ciento veintitrés trillones '
                        'ciento veintitrés mil ciento veintitrés billones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis millones '
                        'ciento veintitrés mil cuatrocientos cincuenta y seis con 67/100')

    def test_value_error_raised_on_invalid_number(self):
        self.assertRaises(ValueError, sing, 10 ** 30)

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before>#!/usr/bin/env python
#
# Copyright 2011 Bryan Goldstein
#

"""
The system currently known as Socrenchus is designed to be a personal tutor
that not only caters to every student individually to help them learn, it also
caters to every professor helping them build up the areas of their course that
need work.

This is the database model and core logic.
"""

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext.db import polymodel
from search import *
from random import shuffle

##########################
## Core Model and Logic ##
##########################

class Connection(db.Model):
  """
  Models a graph edge.
  """
  source = db.ReferenceProperty(db.Model, collection_name="outgoing")
  target = db.ReferenceProperty(db.Model, collection_name="incoming")
  weight = db.IntegerProperty(default=0)

class Question(Searchable, db.Model):
  """
  Models a question.
  """
  author = db.UserProperty(auto_current_user_add = True)
  value = db.TextProperty()
  answers = db.ListProperty(db.Key)
# incoming = db.Query(Connection<Answer>)
# outgoing = db.Query(Connection<Question>)
# assignments = db.Query(aQuestion)

class Answer(db.Model):
  """
  Models an answer.
  """
  author = db.UserProperty(auto_current_user_add = True)
  value = db.StringProperty()
  correctness = db.FloatProperty(default=0.0) # probability of answer being correct
  confidence = db.FloatProperty(default=0.0)  # probability of grading being correct
  question = db.ReferenceProperty(Question)
  graders = db.ListProperty(users.User) # FIXME: seems out of place
# outgoing = [db.Query(Connection<Question>)]

  def __json__(self):
    properties = self.properties().items()
    output = {}
    for field, value in properties:
      if field in ['value', 'correctness', 'author']:
        output[field] = getattr(self, field)
    return output

##########################
## User Model and Logic ##
##########################

class Assignment(polymodel.PolyModel):
  """
  Models a generic assignment
  """
  @classmethod
  def assign(cls, item, user=None):
    """
    Assign an object if it isn't already assigned
    """
    if not user:
      user = users.User()
    instance = cls.getInstance(item, user)
    if not instance:
      instance = cls(parent=item)
      instance.user = user
      instance.put()
      instance.prepare()
    return instance

  @classmethod
  def getInstance(cls, item, user=None):
    """
    Get an instance from the assigned object.
    """
    q = cls.all()
    q = q.ancestor(item)
    return q.filter('user =', user).get()

  def prepare(self):
    self.put()

  user = db.UserProperty(auto_current_user_add = True)
  time = db.DateTimeProperty(auto_now_add = True)
# parent = db.ReferenceProperty(db.Model)

class aQuestion(Assignment):
  """
  Models user specific question data.
  """
  liked = db.BooleanProperty(default = False)
  score = db.FloatProperty(default=0.0)
# user = db.UserProperty(auto_current_user = True)
# parent = db.ReferenceProperty(Question)

  def __json__(self):
    properties = self.properties().items()
    output = {}
    for field, value in properties:
      output[field] = getattr(self, field)
    output['key'] = str(self.key())
    if self.parent():
      output['question'] = self.parent()
    return output

class aNumericAnswerQuestion(aQuestion):
  """
  Handles questions that evaluate numerically.
  """
  pass

class aShortAnswerQuestion(aQuestion):
  answer = db.ReferenceProperty(Answer)
  def submitAnswer(self, myAnswer):
    """
    Answers the question if it hasn't been answered.
    """
    if self.answer:
      return False
    # get user (login is required in app.yaml)
    u = users.User()

    # Generate the assesment question
    q = None
    teacherQ = True
    if len(self.parent().answers) >= 5:
      q = aGraderQuestion.assign(self.parent())
    else:
      teacherQ = None
      for cgq in aConfidentGraderQuestion.all().filter('user =', self.parent().author).ancestor(self.parent()):
        if not cgq.answer:
          teacherQ = cgq

    # Create the user's answer
    self.answer = Answer(value=myAnswer).put()
    self.parent().answers.append(self.answer.key())
    self.parent().put()

    # Assign the teacher to grade
    if not teacherQ:
      aConfidentGraderQuestion.assign(self.parent(), self.parent().author)

    result = [self]

    if q:
      # Attach the question to the answer
      Connection(source=self.answer, target=q).put()
      result.append(q)

    self.put()

    # assign the grading question
    return result

class aMultipleAnswerQuestion(aQuestion):
  answers = db.ListProperty(db.Key)
  answer = db.ListProperty(db.Key)
  def prepare(self):
    """
    Assigns the answers from the question.
    """
    myAnswers = []
    for i in self.parent().answers:
      myAnswers.append(i)

    shuffle(myAnswers)
    self.answers = myAnswers
    self.put()

  def submitAnswer(self, answer):
    """
    Answers the question if it hasn't been answered.
    """
    if self.answer:
      return False

    self.answer = []
    for a in self.answers:
      ans = Answer.get(a)
      if ans.value in answer:
        self.answer.append(a)

    self.put()

    return [self]

class aMultipleChoiceQuestion(aMultipleAnswerQuestion):
  pass

class aGraderQuestion(aMultipleAnswerQuestion):
  """
  Used to grade short answer questions.
  """
  def prepare(self):
    """
    Assigns the answers from the question.
    """
    answers = []
    for ans in self.parent().answers:
      answers.append(ans)

    shuffle(answers)

    # All answers waiting to be graded by count
    answerCount = {}
    query = aGraderQuestion.all().ancestor(self.parent())
    for a in answers:
      answerCount[a] = 0
    for g in query:
      for a in g.answers:
        if a in answerCount.keys():
          answerCount[a] += 1
        else:
          answerCount[a] = 1

    # Find out how many assignments per answer to accept and still have enough
    target = min(answerCount.values())
    i = answerCount.values().count(target)
    while i < 5:
      target += 1
      i += answerCount.values().count(target)

    # Assign the answers
    i = 0
    for a in answers:
      if i >= 5:
        break
      if answerCount[a] <= target:
        self.answers.append(a)
        i += 1

    self.put()

  def submitAnswer(self, answer):
    """
    Runs the grading algorithm after answer is submitted.
    """
    if self.answer:
      return False

    # submit the answer like normal
    tmp = aMultipleAnswerQuestion.submitAnswer(self, answer)

    # grade
    self.grade()

    return tmp

  def grade(self):
    confidence = 0.0
    normalizer = 0.0
    answers = []
    for a in self.answers:
      ans = Answer.get(a)
      answers.append(ans)
      normalizer += ans.confidence
      markedCorrect = (ans.key() in self.answer)
      answerCorrect = (ans.correctness > 0.65)
      if markedCorrect == answerCorrect:
        confidence += ans.confidence
    if normalizer != 0:
      confidence /= float(normalizer)

    # grade with new found confidence
    for a in answers:
      markedCorrect = (a.key() in self.answer)
      numGraders = len(a.graders)
      tmp = numGraders*(a.confidence * a.correctness)
      tmp += confidence * float(markedCorrect)
      if (confidence + numGraders*a.confidence) != 0:
        tmp /= (confidence + numGraders*a.confidence)
      a.correctness = tmp
      a.confidence = (confidence + (a.confidence * numGraders)) / (1 + numGraders)
      a.graders.append(users.User())
      a.put()

    self.score = confidence
    self.put()

class aConfidentGraderQuestion(aMultipleChoiceQuestion):
  """
  Allows the creator of a short answer question to set the baseline.
  """
  answerInQuestion = db.ReferenceProperty(Answer)
  @classmethod
  def getInstance(cls, item, user=None):
    """
    Get an instance from the assigned question.
    """
    q = cls.all()
    q = q.ancestor(item)
    return q.filter('user =', user).filter('answer =',None).get()

  def prepare(self):
    """
    Chooses the lowest confidence answer to grade.
    """
    minAns = Answer()
    minAns.confidence = 1.0
    for ans in self.parent().answers:
      a = Answer.get(ans)
      if a.confidence < minAns.confidence:
        minAns = a
      if minAns.confidence == 0.0:
        break

    if not minAns.value:
      self.delete()
      return None

    # add the answer in question
    self.answerInQuestion = minAns.key()

    # add the answers
    answers = [
      'Definetly Correct',
      'Not Completely Correct',
      'Not Completely Wrong',
      'Definetly Wrong',
    ]
    for gradingAnswer in answers:
      self.answers.append(Answer(value=gradingAnswer).put())

    self.put()

  def submitAnswer(self, answer):
    """
    Submits the grade, regrades neighbors, and assigns
    next ConfidentGraderQuestion.
    """

    # submit the grade
    a = Answer.get(self.answers[0])
    a.correctness = {
      'Definetly Correct': 1.0,
      'Not Completely Correct': 0.75,
      'Not Completely Wrong': 0.5,
      'Definetly Wrong': 0.0,
    }[answer]
    a.confidence = 1.0
    a.put()

    self.answer.append(Answer(value=answer).put())
    self.put()

    # find neighbors
    # TODO: Go out a certain radius
    query = aGraderQuestion.all().ancestor(self.parent()).filter('answers =',a.key())
    for q in query:
      q.grade()

    # assign next ConfidentGraderQuestion
    result = [self]
    next = aConfidentGraderQuestion.assign(self.parent(), self.parent().author)
    if next.answerInQuestion:
      result.append(next)

    return result<commit_msg>fixed confident grader grading bug.<commit_after>#!/usr/bin/env python
#
# Copyright 2011 Bryan Goldstein
#

"""
The system currently known as Socrenchus is designed to be a personal tutor
that not only caters to every student individually to help them learn, it also
caters to every professor helping them build up the areas of their course that
need work.

This is the database model and core logic.
"""

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext.db import polymodel
from search import *
from random import shuffle

##########################
## Core Model and Logic ##
##########################

class Connection(db.Model):
  """
  Models a graph edge.
  """
  source = db.ReferenceProperty(db.Model, collection_name="outgoing")
  target = db.ReferenceProperty(db.Model, collection_name="incoming")
  weight = db.IntegerProperty(default=0)

class Question(Searchable, db.Model):
  """
  Models a question.
  """
  author = db.UserProperty(auto_current_user_add = True)
  value = db.TextProperty()
  answers = db.ListProperty(db.Key)
# incoming = db.Query(Connection<Answer>)
# outgoing = db.Query(Connection<Question>)
# assignments = db.Query(aQuestion)

class Answer(db.Model):
  """
  Models an answer.
  """
  author = db.UserProperty(auto_current_user_add = True)
  value = db.StringProperty()
  correctness = db.FloatProperty(default=0.0) # probability of answer being correct
  confidence = db.FloatProperty(default=0.0)  # probability of grading being correct
  question = db.ReferenceProperty(Question)
  graders = db.ListProperty(users.User) # FIXME: seems out of place
# outgoing = [db.Query(Connection<Question>)]

  def __json__(self):
    properties = self.properties().items()
    output = {}
    for field, value in properties:
      if field in ['value', 'correctness', 'author']:
        output[field] = getattr(self, field)
    return output

##########################
## User Model and Logic ##
##########################

class Assignment(polymodel.PolyModel):
  """
  Models a generic assignment
  """
  @classmethod
  def assign(cls, item, user=None):
    """
    Assign an object if it isn't already assigned
    """
    if not user:
      user = users.User()
    instance = cls.getInstance(item, user)
    if not instance:
      instance = cls(parent=item)
      instance.user = user
      instance.put()
      instance = instance.prepare()
    return instance

  @classmethod
  def getInstance(cls, item, user=None):
    """
    Get an instance from the assigned object.
    """
    q = cls.all()
    q = q.ancestor(item)
    return q.filter('user =', user).get()

  def prepare(self):
    self.put()
    return self

  user = db.UserProperty(auto_current_user_add = True)
  time = db.DateTimeProperty(auto_now_add = True)
# parent = db.ReferenceProperty(db.Model)

class aQuestion(Assignment):
  """
  Models user specific question data.
  """
  liked = db.BooleanProperty(default = False)
  score = db.FloatProperty(default=0.0)
# user = db.UserProperty(auto_current_user = True)
# parent = db.ReferenceProperty(Question)

  def __json__(self):
    properties = self.properties().items()
    output = {}
    for field, value in properties:
      output[field] = getattr(self, field)
    output['key'] = str(self.key())
    if self.parent():
      output['question'] = self.parent()
    return output

class aNumericAnswerQuestion(aQuestion):
  """
  Handles questions that evaluate numerically.
  """
  pass

class aShortAnswerQuestion(aQuestion):
  answer = db.ReferenceProperty(Answer)
  def submitAnswer(self, myAnswer):
    """
    Answers the question if it hasn't been answered.
    """
    if self.answer:
      return False
    # get user (login is required in app.yaml)
    u = users.User()

    # Generate the assesment question
    q = None
    teacherQ = True
    if len(self.parent().answers) >= 5:
      q = aGraderQuestion.assign(self.parent())
    else:
      teacherQ = None
      for cgq in aConfidentGraderQuestion.all().filter('user =', self.parent().author).ancestor(self.parent()):
        if not cgq.answer:
          teacherQ = cgq

    # Create the user's answer
    self.answer = Answer(value=myAnswer).put()
    self.parent().answers.append(self.answer.key())
    self.parent().put()

    # Assign the teacher to grade
    if not teacherQ:
      aConfidentGraderQuestion.assign(self.parent(), self.parent().author)

    result = [self]

    if q:
      # Attach the question to the answer
      Connection(source=self.answer, target=q).put()
      result.append(q)

    self.put()

    # assign the grading question
    return result

class aMultipleAnswerQuestion(aQuestion):
  answers = db.ListProperty(db.Key)
  answer = db.ListProperty(db.Key)
  def prepare(self):
    """
    Assigns the answers from the question.
    """
    myAnswers = []
    for i in self.parent().answers:
      myAnswers.append(i)

    shuffle(myAnswers)
    self.answers = myAnswers
    self.put()
    return self

  def submitAnswer(self, answer):
    """
    Answers the question if it hasn't been answered.
    """
    if self.answer:
      return False

    self.answer = []
    for a in self.answers:
      ans = Answer.get(a)
      if ans.value in answer:
        self.answer.append(a)

    self.put()

    return [self]

class aMultipleChoiceQuestion(aMultipleAnswerQuestion):
  pass

class aGraderQuestion(aMultipleAnswerQuestion):
  """
  Used to grade short answer questions.
  """
  def prepare(self):
    """
    Assigns the answers from the question.
    """
    answers = []
    for ans in self.parent().answers:
      answers.append(ans)

    shuffle(answers)

    # All answers waiting to be graded by count
    answerCount = {}
    query = aGraderQuestion.all().ancestor(self.parent())
    for a in answers:
      answerCount[a] = 0
    for g in query:
      for a in g.answers:
        if a in answerCount.keys():
          answerCount[a] += 1
        else:
          answerCount[a] = 1

    # Find out how many assignments per answer to accept and still have enough
    target = min(answerCount.values())
    i = answerCount.values().count(target)
    while i < 5:
      target += 1
      i += answerCount.values().count(target)

    # Assign the answers
    i = 0
    for a in answers:
      if i >= 5:
        break
      if answerCount[a] <= target:
        self.answers.append(a)
        i += 1

    self.put()
    return self

  def submitAnswer(self, answer):
    """
    Runs the grading algorithm after answer is submitted.
    """
    if self.answer:
      return False

    # submit the answer like normal
    tmp = aMultipleAnswerQuestion.submitAnswer(self, answer)

    # grade
    self.grade()

    return tmp

  def grade(self):
    confidence = 0.0
    normalizer = 0.0
    answers = []
    for a in self.answers:
      ans = Answer.get(a)
      answers.append(ans)
      normalizer += ans.confidence
      markedCorrect = (ans.key() in self.answer)
      answerCorrect = (ans.correctness > 0.65)
      if markedCorrect == answerCorrect:
        confidence += ans.confidence
    if normalizer != 0:
      confidence /= float(normalizer)

    # grade with new found confidence
    for a in answers:
      markedCorrect = (a.key() in self.answer)
      numGraders = len(a.graders)
      tmp = numGraders*(a.confidence * a.correctness)
      tmp += confidence * float(markedCorrect)
      if (confidence + numGraders*a.confidence) != 0:
        tmp /= (confidence + numGraders*a.confidence)
      a.correctness = tmp
      a.confidence = (confidence + (a.confidence * numGraders)) / (1 + numGraders)
      a.graders.append(users.User())
      a.put()

    self.score = confidence
    self.put()

class aConfidentGraderQuestion(aMultipleChoiceQuestion):
  """
  Allows the creator of a short answer question to set the baseline.
  """
  answerInQuestion = db.ReferenceProperty(Answer)
  @classmethod
  def getInstance(cls, item, user=None):
    """
    Get an instance from the assigned question.
    """
    q = cls.all()
    q = q.ancestor(item)
    return q.filter('user =', user).filter('answer =',None).get()

  def prepare(self):
    """
    Chooses the lowest confidence answer to grade.
    """
    minAns = Answer()
    minAns.confidence = 1.0
    for ans in self.parent().answers:
      a = Answer.get(ans)
      if a.confidence < minAns.confidence:
        minAns = a
      if minAns.confidence == 0.0:
        break

    if minAns.confidence == 1.0:
      self.delete()
      return None

    # add the answer in question
    self.answerInQuestion = minAns.key()

    # add the answers
    answers = [
      'Definetly Correct',
      'Not Completely Correct',
      'Not Completely Wrong',
      'Definetly Wrong',
    ]
    for gradingAnswer in answers:
      self.answers.append(Answer(value=gradingAnswer).put())

    self.put()
    return self

  def submitAnswer(self, answer):
    """
    Submits the grade, regrades neighbors, and assigns
    next ConfidentGraderQuestion.
    """

    # submit the grade
    a = self.answerInQuestion
    a.correctness = {
      'Definetly Correct': 1.0,
      'Not Completely Correct': 0.75,
      'Not Completely Wrong': 0.5,
      'Definetly Wrong': 0.0,
    }[answer]
    a.confidence = 1.0
    a.put()

    self.answer.append(Answer(value=answer).put())
    self.put()

    # find neighbors
    # TODO: Go out a certain radius
    query = aGraderQuestion.all().ancestor(self.parent()).filter('answers =',a.key())
    for q in query:
      q.grade()

    # assign next ConfidentGraderQuestion
    result = [self]
    next = aConfidentGraderQuestion.assign(self.parent(), self.parent().author)
    if next:
      result.append(next)

    return result<|endoftext|>


<commit_before>#!/usr/bin/python

import time
import logging
import sys

def generate_error(message):
    print '\033[31m\033[1m%s\033[0m\033[39m' % message

try:
    import smbus
except ImportError:
    generate_error('SMBUS not configured properly!')
    sys.exit(1)

import threading
import socket
from thread import *
import time
import json
import threading

wind_sensor = None  # Global wind_sensor variable
angle = 0.0

# Define the socket parameters

HOST = ''
PORT = 8894

connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind socket to local host and port
try:
    connection.bind((HOST, PORT))
except socket.error, msg:
    generate_error('Bind failed. Error Code: ' + str(msg[0]) + ' Message ' \
        + msg[1])
    sys.exit()

print 'Wind sensor socket bind complete'

class WindSensor(threading.Thread):

    address = 0x48  # Address the port runs on
    analogs = [0x4000, 0x5000, 0x6000, 0x7000]
    normal = 0x0418

    max = 1200.0
    min = 200.0

    def switch_bits(self, input):

        # Flips the bits of the input and returns the result
        return (input >> 8) + ((input & 0x00ff) << 8)

    def set_data_channel(self, input, bus):
        bus.write_word_data(self.address, 0x01, 0x1C40)
        time.sleep(0.01)

    def read_data(self, bus):

        # Read data from the bus
        time.sleep(0.01)
        return self.switch_bits(bus.read_word_data(self.address, 0)) >> 4

    def setup(self, input, bus):

        # Write values to the registers
        bus.write_word_data(self.address, 0x02, self.switch_bits(input << 4))
        time.sleep(0.01)
        bus.write_word_data(self.address, 0x03, self.switch_bits(0x7fff))
        time.sleep(0.01)

    def calculate_angle(self, input):

        # Alter the maximum and minimum values during calibration
        if input > self.max:
            self.max = input

        if input < self.min:
            self.min = input

        a = self.max - self.min

        # Return the measured value as a proportionally scaled angle
        return (input - self.min) * (360.0 / a)

    def run(self):
        global angle  # Bring the angle into scope

        try:
            bus = smbus.SMBus(0x01)
            self.setup(1500, bus)
            self.set_data_channel(self.analogs[0], bus)

            while True:
                angle = self.calculate_angle(self.read_data(bus))
                time.sleep(0.1)
        except IOError:
            generate_error('IO Error: device cannot be read, check your wiring or run as root')

# Create and start the wind_sensor thread
wind_sensor = WindSensor()
wind_sensor.daemon = True # Needed to make the thread shutdown correctly
wind_sensor.start()

# Start listening on socket
connection.listen(10)

# Function for handling connections; will be used to create threads

def clientthread(conn):

    # Infinite loop so that function do not terminate and thread do not end

    while True:

        # Receive data from the client
        data = conn.recv(1024)
        if not data:
            break

        conn.sendall(json.dumps(angle).encode('utf-8'))

    # close the connection if the client if the client and server connection is interfered
    conn.close()

# Main loop to keep the server process going
while True:

    try:
        # Wait to accept a connection in a blocking call
        (conn, addr) = connection.accept()
        print 'Connected with ' + addr[0] + ':' + str(addr[1])

        # Start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function
        start_new_thread(clientthread, (conn, ))

    except KeyboardInterrupt, socket.error:
        connection.shutdown(socket.SHUT_RDWR)
        connection.close()
        break

            <commit_msg>Added calibration reminder to wind sensor script<commit_after>#!/usr/bin/python

import time
import logging
import sys

def generate_error(message):
    print '\033[31m\033[1m%s\033[0m\033[39m' % message

try:
    import smbus
except ImportError:
    generate_error('SMBUS not configured properly!')
    sys.exit(1)

import threading
import socket
from thread import *
import time
import json
import threading

wind_sensor = None  # Global wind_sensor variable
angle = 0.0

# Define the socket parameters

HOST = ''
PORT = 8894

connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind socket to local host and port
try:
    connection.bind((HOST, PORT))
except socket.error, msg:
    generate_error('Bind failed. Error Code: ' + str(msg[0]) + ' Message ' \
        + msg[1])
    sys.exit()

print 'Wind sensor socket bind complete'

class WindSensor(threading.Thread):

    address = 0x48  # Address the port runs on
    analogs = [0x4000, 0x5000, 0x6000, 0x7000]
    normal = 0x0418

    max = 1200.0
    min = 200.0

    def switch_bits(self, input):

        # Flips the bits of the input and returns the result
        return (input >> 8) + ((input & 0x00ff) << 8)

    def set_data_channel(self, input, bus):
        bus.write_word_data(self.address, 0x01, 0x1C40)
        time.sleep(0.01)

    def read_data(self, bus):

        # Read data from the bus
        time.sleep(0.01)
        return self.switch_bits(bus.read_word_data(self.address, 0)) >> 4

    def setup(self, input, bus):

        # Write values to the registers
        bus.write_word_data(self.address, 0x02, self.switch_bits(input << 4))
        time.sleep(0.01)
        bus.write_word_data(self.address, 0x03, self.switch_bits(0x7fff))
        time.sleep(0.01)

    def calculate_angle(self, input):

        # Alter the maximum and minimum values during calibration
        if input > self.max:
            self.max = input

        if input < self.min:
            self.min = input

        a = self.max - self.min

        # Return the measured value as a proportionally scaled angle
        return (input - self.min) * (360.0 / a)

    def run(self):
        global angle  # Bring the angle into scope

        generate_error('Remember to calibrate the wind sensor before use!')

        try:
            bus = smbus.SMBus(0x01)
            self.setup(1500, bus)
            self.set_data_channel(self.analogs[0], bus)

            while True:
                angle = self.calculate_angle(self.read_data(bus))
                time.sleep(0.1)
        except IOError:
            generate_error('IO Error: device cannot be read, check your wiring or run as root')

# Create and start the wind_sensor thread
wind_sensor = WindSensor()
wind_sensor.daemon = True # Needed to make the thread shutdown correctly
wind_sensor.start()

# Start listening on socket
connection.listen(10)

# Function for handling connections; will be used to create threads

def clientthread(conn):

    # Infinite loop so that function do not terminate and thread do not end

    while True:

        # Receive data from the client
        data = conn.recv(1024)
        if not data:
            break

        conn.sendall(json.dumps(angle).encode('utf-8'))

    # close the connection if the client if the client and server connection is interfered
    conn.close()

# Main loop to keep the server process going
while True:

    try:
        # Wait to accept a connection in a blocking call
        (conn, addr) = connection.accept()
        print 'Connected with ' + addr[0] + ':' + str(addr[1])

        # Start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function
        start_new_thread(clientthread, (conn, ))

    except KeyboardInterrupt, socket.error:
        connection.shutdown(socket.SHUT_RDWR)
        connection.close()
        break

            <|endoftext|>


<commit_before>from coyote_framework.config.host_config_manager import HostConfigManager

__author__ = 'justin'

import os

def ssh(cmd):
    cmd = "ssh %(sshHost)s \"%(cmd)s\"" % {
        'sshHost': HostConfigManager().get_host().hostname,
        'cmd':     cmd,
        }
    return cmd

def system(cmd):
    os.system(cmd)<commit_msg>Remove unedded files<commit_after><|endoftext|>


<commit_before># -*- coding: utf-8  -*-
"""
Exception classes used throughout the framework.
"""
#
# (C) Pywikibot team, 2008
#
# Distributed under the terms of the MIT license.
#
__version__ = '$Id$'

from pywikibot.tools import UnicodeMixin

# TODO: These are copied from wikipedia.py; not certain that all of them
# will be needed in the rewrite.

class Error(Exception, UnicodeMixin):
    """Wikipedia error"""
    def __init__(self, arg):
        self.unicode = arg

    def __unicode__(self):
        return self.unicode

class PageRelatedError(Error):
    """Abstract Exception, used when the Exception concerns a particular
    Page, and when a generic message can be written once for all"""
    # Preformated UNICODE message where the page title will be inserted
    # Override this in subclasses.
    # u"Oh noes! Page %s is too funky, we should not delete it ;("
    message = None

    def __init__(self, page):
        """
        @param page
        @type page: Page object
        """
        if self.message is None:
            raise Error("PageRelatedError is abstract. Can't instantiate it!")
        super(PageRelatedError, self).__init__(self.message % page)
        self._page = page

    def getPage(self):
        return self._page

class NoUsername(Error):
    """Username is not in user-config.py"""

class NoPage(PageRelatedError):
    """Page does not exist"""
    message = u"Page %s doesn't exist."

class NoSuchSite(Error):
    """Site does not exist"""

class IsRedirectPage(PageRelatedError):
    """Page is a redirect page"""
    message = u"Page %s is a redirect page."

class IsNotRedirectPage(PageRelatedError):
    """Page is not a redirect page"""
    message = u"Page %s is not a redirect page."

class CircularRedirect(Error):
    """Page is a circular redirect

    Exception argument is the redirect target; this may be the same title
    as this page or a different title (in which case the target page directly
    or indirectly redirects back to this one)

    """

class InvalidTitle(Error):
    """Invalid page title"""

class LockedPage(PageRelatedError):
    """Page is locked"""
    message = u"Page %s is locked."

class SectionError(Error):
    """The section specified by # does not exist"""

class PageNotSaved(Error):
    """Saving the page has failed"""

class EditConflict(PageNotSaved):
    """There has been an edit conflict while uploading the page"""

class SpamfilterError(PageNotSaved):
    """Saving the page has failed because the MediaWiki spam filter detected a blacklisted URL."""
    def __init__(self, arg):
        super(SpamfilterError, self).__init__(u'MediaWiki spam filter has been triggered')
        self.url = arg
        self.args = arg,

class ServerError(Error):
    """Got unexpected server response"""

class FatalServerError(ServerError):
    """A fatal server error that's not going to be corrected by just sending
    the request again."""

class Server504Error(Error):
    """Server timed out with http 504 code"""

class BadTitle(Error):
    """Server responded with BadTitle."""

# UserBlocked exceptions should in general not be caught. If the bot has
# been blocked, the bot operator should address the reason for the block
# before continuing.

class UserBlocked(Error):
    """Your username or IP has been blocked"""

class PageNotFound(Error):
    """Page not found in list"""

class CaptchaError(Error):
    """Captcha is asked and config.solve_captcha == False."""

class UploadWarning(Error):
    """Upload failed with a warning message (passed as the argument)."""

class AutoblockUser(Error):
    """
    The class AutoblockUserError is an exception that is raised whenever
    an action is requested on a virtual autoblock user that's not available
    for him (i.e. roughly everything except unblock).
    """

class UserActionRefuse(Error):
    pass

class WikiBaseError(Error):
    pass

class CoordinateGlobeUnknownException(WikiBaseError, NotImplementedError):
    """ This globe is not implemented yet in either WikiBase or pywikibot """
<commit_msg>(bug 66012) Reorder inheritance of Error class<commit_after># -*- coding: utf-8  -*-
"""
Exception classes used throughout the framework.
"""
#
# (C) Pywikibot team, 2008
#
# Distributed under the terms of the MIT license.
#
__version__ = '$Id$'

from pywikibot.tools import UnicodeMixin

# TODO: These are copied from wikipedia.py; not certain that all of them
# will be needed in the rewrite.

class Error(UnicodeMixin, Exception):
    """Wikipedia error"""
    # NOTE: UnicodeMixin must be the first object Error class is derived from.
    def __init__(self, arg):
        self.unicode = arg

    def __unicode__(self):
        return self.unicode

class PageRelatedError(Error):
    """Abstract Exception, used when the Exception concerns a particular
    Page, and when a generic message can be written once for all"""
    # Preformated UNICODE message where the page title will be inserted
    # Override this in subclasses.
    # u"Oh noes! Page %s is too funky, we should not delete it ;("
    message = None

    def __init__(self, page):
        """
        @param page
        @type page: Page object
        """
        if self.message is None:
            raise Error("PageRelatedError is abstract. Can't instantiate it!")
        super(PageRelatedError, self).__init__(self.message % page)
        self._page = page

    def getPage(self):
        return self._page

class NoUsername(Error):
    """Username is not in user-config.py"""

class NoPage(PageRelatedError):
    """Page does not exist"""
    message = u"Page %s doesn't exist."

class NoSuchSite(Error):
    """Site does not exist"""

class IsRedirectPage(PageRelatedError):
    """Page is a redirect page"""
    message = u"Page %s is a redirect page."

class IsNotRedirectPage(PageRelatedError):
    """Page is not a redirect page"""
    message = u"Page %s is not a redirect page."

class CircularRedirect(Error):
    """Page is a circular redirect

    Exception argument is the redirect target; this may be the same title
    as this page or a different title (in which case the target page directly
    or indirectly redirects back to this one)

    """

class InvalidTitle(Error):
    """Invalid page title"""

class LockedPage(PageRelatedError):
    """Page is locked"""
    message = u"Page %s is locked."

class SectionError(Error):
    """The section specified by # does not exist"""

class PageNotSaved(Error):
    """Saving the page has failed"""

class EditConflict(PageNotSaved):
    """There has been an edit conflict while uploading the page"""

class SpamfilterError(PageNotSaved):
    """Saving the page has failed because the MediaWiki spam filter detected a
    blacklisted URL.
    """
    def __init__(self, arg):
        super(SpamfilterError, self).__init__(
            u'MediaWiki spam filter has been triggered')
        self.url = arg
        self.args = arg,

class ServerError(Error):
    """Got unexpected server response"""

class FatalServerError(ServerError):
    """A fatal server error that's not going to be corrected by just sending
    the request again."""

class Server504Error(Error):
    """Server timed out with http 504 code"""

class BadTitle(Error):
    """Server responded with BadTitle."""

# UserBlocked exceptions should in general not be caught. If the bot has
# been blocked, the bot operator should address the reason for the block
# before continuing.

class UserBlocked(Error):
    """Your username or IP has been blocked"""

class PageNotFound(Error):
    """Page not found in list"""

class CaptchaError(Error):
    """Captcha is asked and config.solve_captcha == False."""

class UploadWarning(Error):
    """Upload failed with a warning message (passed as the argument)."""

class AutoblockUser(Error):
    """
    The class AutoblockUserError is an exception that is raised whenever
    an action is requested on a virtual autoblock user that's not available
    for him (i.e. roughly everything except unblock).
    """

class UserActionRefuse(Error):
    pass

class WikiBaseError(Error):
    pass

class CoordinateGlobeUnknownException(WikiBaseError, NotImplementedError):
    """ This globe is not implemented yet in either WikiBase or pywikibot """
<|endoftext|>


<commit_before>#!/usr/bin/python

from pprint import pprint
import pandas as pd
import numpy as np
import scipy.sparse as sp
from sklearn.feature_extraction import DictVectorizer

movies = pd.read_csv("merged.csv")
movies["movieIdx"] = movies.index # used in sparse matrix

ratings = pd.read_csv("ratings.csv")

ratings["userIdx"] = ratings["userId"] - 1
ratings = pd.merge(ratings, movies, on = "movieId", how = "inner")
ratings_matrix = sp.coo_matrix((ratings['rating'].values,
        (ratings['movieIdx'].values, ratings['userIdx'].values)))

movies["genres_x"] = movies["genres_x"].str.replace("|", ",", regex=False)
movies["timestamp"] = pd.to_datetime(movies["date"]).fillna(pd.Timestamp(0.)).astype('int64') // 10**9

numerical_columns = [
    "timestamp",
    "runtime",
    "revenue",
    "budget",
    "popularity",
    "average_vote",
    "num_votes",
]

category_columns = [
    "genres_x",
    "genres_y",
    #"keywords",
    #"director",
    #"cast",
    #"production_companies",
    "production_countries",
    "language",
]

for col in category_columns:
    movies[col] = movies[col].str.split(", ?")

selected = movies[numerical_columns + category_columns].to_dict(orient = 'records')
features_vectorizer = DictVectorizer()
features_matrix = features_vectorizer.fit_transform(selected)

import pickle

pickle.dump(features_vectorizer, open("movies_features_vectorizer.pickle", "wb"))
pickle.dump(features_matrix,     open("movie_features_matrix.pickle", "wb"))
pickle.dump(ratings_matrix,      open("ratings_matrix.pickle", "wb"))<commit_msg>WIP: creating movielens features<commit_after>#!/usr/bin/python

from pprint import pprint
from numpy.core.fromnumeric import nonzero
import pandas as pd
import numpy as np
import scipy.sparse as sp
from sklearn.feature_extraction import DictVectorizer
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
import smurff

movies = pd.read_csv("merged.csv")
movies["movieIdx"] = movies.index # used in sparse matrix

ratings = pd.read_csv("ratings.csv")

ratings["userIdx"] = ratings["userId"] - 1
ratings = pd.merge(ratings, movies, on = "movieId", how = "inner")
ratings_matrix = sp.coo_matrix((ratings['rating'].values,
        (ratings['movieIdx'].values, ratings['userIdx'].values))).tocsr()

movies["genres_x"] = movies["genres_x"].str.replace("|", ",", regex=False)
movies["timestamp"] = pd.to_datetime(movies["date"]).fillna(pd.Timestamp(0.)).astype('int64') // 10**9

lang_ids = { l : i for i,l in enumerate(movies["language"].unique()) }
movies["language_id"] = movies["language"].map(lang_ids)

numerical_columns = [
    "timestamp",
    "runtime",
    "revenue",
    "budget",
    "popularity",
    "average_vote",
    "num_votes",
    "language_id",
]

category_columns = [
    "genres_x",
    "genres_y",
    #"keywords",
    #"director",
    #"cast",
    #"production_companies",
    "production_countries",
]

movies[numerical_columns] = SimpleImputer(missing_values=np.nan, strategy='mean').fit_transform(movies[numerical_columns])
movies[numerical_columns] = StandardScaler().fit_transform(movies[numerical_columns])
movies.to_csv("movies_normalized.csv")

### ---  category

movies[category_columns] = movies[category_columns].fillna("")

for col in category_columns:
    movies[col] = movies[col].str.split(", ?")

features_vectorizer = DictVectorizer()
categories_dict = movies[category_columns].to_dict(orient = 'records')
categories_matrix = features_vectorizer.fit_transform(categories_dict)

for col,name in enumerate(features_vectorizer.get_feature_names()):
    data = categories_matrix[:,col].data
    num_nan = np.isnan(data).sum()
    if num_nan > 0:
        print(f"{name}: {num_nan} / {len(data)} - {data}")

features_matrix = sp.hstack((categories_matrix, sp.csc_matrix(movies[numerical_columns])))

import pickle

pickle.dump(features_vectorizer, open("movies_features_vectorizer.pickle", "wb"))
pickle.dump(features_matrix,     open("movie_features_matrix.pickle", "wb"))
pickle.dump(ratings_matrix,      open("ratings_matrix.pickle", "wb"))

smurff.matrix_io.write_matrix("ratings.sdm", ratings_matrix)
smurff.matrix_io.write_matrix("features.sdm", features_matrix)

#--- only save 1000 movies and 1000 users
ratings_matrix = ratings_matrix.tocsr()
features_matrix = features_matrix.tocsr()

nnz_per_movie = [ ratings_matrix[r,:].nnz for r in range(ratings_matrix.shape[0]) ]
popular_movies = np.argsort(nnz_per_movie)
ratings_matrix_1k = ratings_matrix[popular_movies[-1000:], :]
features_matrix_1k = features_matrix[popular_movies[-1000:], :]

ratings_matrix_1k = ratings_matrix_1k.tocsc()
nnz_per_user = [ ratings_matrix_1k[:,r].nnz for r in range(ratings_matrix_1k.shape[1]) ]
popular_users = np.argsort(nnz_per_user)
ratings_matrix_1k = ratings_matrix_1k[:, popular_users[-1000:]]

smurff.matrix_io.write_matrix("ratings_1k.sdm", ratings_matrix_1k)
smurff.matrix_io.write_matrix("features_1k.sdm", features_matrix_1k)<|endoftext|>


<commit_before>from flask import Flask, request, redirect, render_template, flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.utils import secure_filename

import collections
import fnmatch
import math
import os
import sys
from lxml import etree, objectify

UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = set(['xml'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

class METS(db.model):
    id = db.Column(db.Integer, primary_key=True)
    metsfile = db.Column(db.String(120), unique=True)
    metsdict = db.Column(PickleType)

    def __init__(self, metsfile, metsdict):
        self.metsfile = metsfile
        self.metsdict = metsdict

    def __repr__(self):
        return '<File %r>' % self.metsfile

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def convert_size(size):
    # convert size to human-readable form
    size = int(size)
    size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
    i = int(math.floor(math.log(size,1024)))
    p = math.pow(1024,i)
    s = round(size/p)
    s = str(s)
    s = s.replace('.0', '')
    return '%s %s' % (s,size_name[i])

def mets_to_list_of_dicts(mets_path):
    # create list
    original_files = []

    # open xml file and strip namespaces
    tree = etree.parse(mets_path)
    root = tree.getroot()

    # strip namespaces from XML
    for elem in root.getiterator():
        if not hasattr(elem.tag, 'find'): continue  # (1)
        i = elem.tag.find('}')
        if i >= 0:
            elem.tag = elem.tag[i+1:]
    objectify.deannotate(root, cleanup_namespaces=True)

    # store names and xpaths of desired info from individual files in tuples and convert to ordered dict
    xml_file_elements = (('filepath', './techMD/mdWrap/xmlData/object/originalName'),
                    ('uuid', './techMD/mdWrap/xmlData/object/objectIdentifier/objectIdentifierValue'),
                    ('sha256', './techMD/mdWrap/xmlData/object/objectCharacteristics/fixity/messageDigest'),
                    ('bytes', './techMD/mdWrap/xmlData/object/objectCharacteristics/size'),
                    ('format', './techMD/mdWrap/xmlData/object/objectCharacteristics/format/formatDesignation/formatName'),
                    ('version', './techMD/mdWrap/xmlData/object/objectCharacteristics/format/formatDesignation/formatVersion'),
                    ('puid', './techMD/mdWrap/xmlData/object/objectCharacteristics/format/formatRegistry/formatRegistryKey'),
                    ('fits_modified', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/fits/toolOutput/tool[@name="Exiftool"]/exiftool/FileModifyDate'),
                    ('fits_created', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/fits/fileinfo/created[@toolname="Exiftool"]'),
                    ('fileutil_mimetype', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/fits/toolOutput/tool[@name="file utility"]/fileUtilityOutput/mimetype'),
                    ('fileutil_format', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/fits/toolOutput/tool[@name="file utility"]/fileUtilityOutput/format'),
                    ('exiftool_mimetype', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/RDF/Description/MIMEType'),
                    ('exiftool_format', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/RDF/Description/FileType'),
                    ('mediainfo_modified', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/Mediainfo/File/track[@type="General"]/File_last_modification_date__local_'),
                    ('mediainfo_mimetype', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/Mediainfo/File/track[@type="General"]/Internet_media_type'),
                    ('mediainfo_format', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/Mediainfo/File/track[@type="General"]/Format'))
    xml_file_elements = collections.OrderedDict(xml_file_elements)

    # build xml document root
    mets_root = root

    # gather info for each file in filegroup "original" and write to files.csv
    for target in mets_root.findall(".//fileGrp[@USE='original']/file"):

        # create new dictionary for this item's info
        file_data = {}

        # create new list of dicts for premis events in file_data
        file_data['premis_events'] = []

        # gather amdsec id from filesec
        amdsec_id = target.attrib['ADMID']
        file_data['amdsec_id'] = amdsec_id

        # parse each amdSec
        amdsec_xpath = ".//amdSec[@ID='%s']" % (amdsec_id)
        for target1 in mets_root.findall(amdsec_xpath):

            # iterate over elements and write key, value for each to file_data dictionary
            for key, value in xml_file_elements.items():
                try:
                    file_data['%s' % (key)] = target1.find(value).text
                except AttributeError:
                    file_data['%s' % (key)] = ''

            # parse info for each PREMIS event associated with amdSec
            premis_event_xpath = ".//digiprovMD/mdWrap[@MDTYPE='PREMIS:EVENT']"
            for target2 in target1.findall(premis_event_xpath):

                # create dict to store data
                premis_event = {}

                # create dict for names and xpaths of desired elements
                premis_key_values = {
                    'event_uuid': './xmlData/event/eventIdentifier/eventIdentifierValue',
                    'event_type': './xmlData/event/eventType',
                    'event_datetime': './xmlData/event/eventDateTime',
                    'event_detail': './xmlData/event/eventDetail',
                    'event_outcome': './xmlData/event/eventOutcomeInformation/eventOutcome',
                    'event_detail_note': './xmlData/event/eventOutcomeInformation/eventOutcomeDetail/eventOutcomeDetailNote'
                }

                # iterate over elements and write key, value for each to premis_event dictionary
                for key, value in premis_key_values.items():
                    try:
                        premis_event['%s' % (key)] = target2.find(value).text
                    except AttributeError:
                        premis_event['%s' % (key)] = ''

                # create list of dicts for PREMIS Agents associated with Events
                premis_agents = []

                # parse info for each PREMIS Agent pair associated with Event
                premis_event_xpath = ".//xmlData/event/linkingAgentIdentifier"
                for target3 in target2.findall(premis_event_xpath):

                    premis_agent = {}
                    try:
                        premis_agent['agent_type'] = target3.find('./linkingAgentIdentifierType').text
                    except AttributeError:
                        premis_agent['agent_type'] = ''

                    try:
                        premis_agent['agent_value'] = target3.find('./linkingAgentIdentifierValue').text
                    except AttributeError:
                        premis_agent['agent_value'] = ''

                    # append PREMIS Agent pair to premis_agents
                    premis_agents.append(premis_agent)

                # write premis_agents to premis_events
                premis_event['premis_agents'] = premis_agents

                # write premis_event dict to file_data
                file_data['premis_events'].append(premis_event)

        # format filepath
        file_data['filepath'] = file_data['filepath'].replace('%transferDirectory%', '')

        # format PUID
        file_data['puid'] = "%s" % (file_data['puid'], file_data['puid'])

        # create human-readable size
        file_data['size'] = convert_size(file_data['bytes'])

        # add file_data to list
        original_files.append(file_data)

    return original_files

original_files = []

@app.route("/", methods=['GET', 'POST'])
def index():
    return render_template('upload.html')

@app.route('/originalfiles', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return render_template('upload.html')
        file = request.files['file']
        # if user does not select file, browser also
        # submit a empty part without filename
        if file.filename == '':
            flash('No selected file')
            return render_template('upload.html')
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            mets_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
            global aip_name
            aip_name = os.path.basename(filename)
            global original_files
            original_files = mets_to_list_of_dicts(mets_path) # write details to global original_files list of dicts
            os.remove(os.path.join(app.config['UPLOAD_FOLDER'], filename)) # delte file from uploads folder
            return render_template('originalfiles.html', original_files = original_files, aip_name = aip_name)

@app.route('/file/<UUID>')
def show_file(UUID):
    for original_file in original_files:
        if original_file["uuid"] == UUID:
            target_original_file = original_file
            break
    return render_template('detail.html', original_file=target_original_file, aip_name = aip_name)

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

<commit_msg>Upload file adds db instance<commit_after>from flask import Flask, request, redirect, render_template, flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.utils import secure_filename

import collections
import fnmatch
import math
import os
import sys
from lxml import etree, objectify

UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = set(['xml'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

db.create_all()

class METS(db.model):
    id = db.Column(db.Integer, primary_key=True)
    metsfile = db.Column(db.String(120), unique=True)
    metsdict = db.Column(PickleType)

    def __init__(self, metsfile, metsdict):
        self.metsfile = metsfile
        self.metsdict = metsdict

    def __repr__(self):
        return '<File %r>' % self.metsfile

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def convert_size(size):
    # convert size to human-readable form
    size = int(size)
    size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
    i = int(math.floor(math.log(size,1024)))
    p = math.pow(1024,i)
    s = round(size/p)
    s = str(s)
    s = s.replace('.0', '')
    return '%s %s' % (s,size_name[i])

def mets_to_list_of_dicts(mets_path):
    # create list
    original_files = []

    # get METS file name
    mets_filename = os.path.basename(mets_path)

    # open xml file and strip namespaces
    tree = etree.parse(mets_path)
    root = tree.getroot()

    # strip namespaces from XML
    for elem in root.getiterator():
        if not hasattr(elem.tag, 'find'): continue  # (1)
        i = elem.tag.find('}')
        if i >= 0:
            elem.tag = elem.tag[i+1:]
    objectify.deannotate(root, cleanup_namespaces=True)

    # store names and xpaths of desired info from individual files in tuples and convert to ordered dict
    xml_file_elements = (('filepath', './techMD/mdWrap/xmlData/object/originalName'),
                    ('uuid', './techMD/mdWrap/xmlData/object/objectIdentifier/objectIdentifierValue'),
                    ('sha256', './techMD/mdWrap/xmlData/object/objectCharacteristics/fixity/messageDigest'),
                    ('bytes', './techMD/mdWrap/xmlData/object/objectCharacteristics/size'),
                    ('format', './techMD/mdWrap/xmlData/object/objectCharacteristics/format/formatDesignation/formatName'),
                    ('version', './techMD/mdWrap/xmlData/object/objectCharacteristics/format/formatDesignation/formatVersion'),
                    ('puid', './techMD/mdWrap/xmlData/object/objectCharacteristics/format/formatRegistry/formatRegistryKey'),
                    ('fits_modified', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/fits/toolOutput/tool[@name="Exiftool"]/exiftool/FileModifyDate'),
                    ('fits_created', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/fits/fileinfo/created[@toolname="Exiftool"]'),
                    ('fileutil_mimetype', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/fits/toolOutput/tool[@name="file utility"]/fileUtilityOutput/mimetype'),
                    ('fileutil_format', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/fits/toolOutput/tool[@name="file utility"]/fileUtilityOutput/format'),
                    ('exiftool_mimetype', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/RDF/Description/MIMEType'),
                    ('exiftool_format', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/RDF/Description/FileType'),
                    ('mediainfo_modified', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/Mediainfo/File/track[@type="General"]/File_last_modification_date__local_'),
                    ('mediainfo_mimetype', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/Mediainfo/File/track[@type="General"]/Internet_media_type'),
                    ('mediainfo_format', './techMD/mdWrap/xmlData/object/objectCharacteristics/objectCharacteristicsExtension/Mediainfo/File/track[@type="General"]/Format'))
    xml_file_elements = collections.OrderedDict(xml_file_elements)

    # build xml document root
    mets_root = root

    # gather info for each file in filegroup "original" and write to files.csv
    for target in mets_root.findall(".//fileGrp[@USE='original']/file"):

        # create new dictionary for this item's info
        file_data = {}

        # create new list of dicts for premis events in file_data
        file_data['premis_events'] = []

        # gather amdsec id from filesec
        amdsec_id = target.attrib['ADMID']
        file_data['amdsec_id'] = amdsec_id

        # parse each amdSec
        amdsec_xpath = ".//amdSec[@ID='%s']" % (amdsec_id)
        for target1 in mets_root.findall(amdsec_xpath):

            # iterate over elements and write key, value for each to file_data dictionary
            for key, value in xml_file_elements.items():
                try:
                    file_data['%s' % (key)] = target1.find(value).text
                except AttributeError:
                    file_data['%s' % (key)] = ''

            # parse info for each PREMIS event associated with amdSec
            premis_event_xpath = ".//digiprovMD/mdWrap[@MDTYPE='PREMIS:EVENT']"
            for target2 in target1.findall(premis_event_xpath):

                # create dict to store data
                premis_event = {}

                # create dict for names and xpaths of desired elements
                premis_key_values = {
                    'event_uuid': './xmlData/event/eventIdentifier/eventIdentifierValue',
                    'event_type': './xmlData/event/eventType',
                    'event_datetime': './xmlData/event/eventDateTime',
                    'event_detail': './xmlData/event/eventDetail',
                    'event_outcome': './xmlData/event/eventOutcomeInformation/eventOutcome',
                    'event_detail_note': './xmlData/event/eventOutcomeInformation/eventOutcomeDetail/eventOutcomeDetailNote'
                }

                # iterate over elements and write key, value for each to premis_event dictionary
                for key, value in premis_key_values.items():
                    try:
                        premis_event['%s' % (key)] = target2.find(value).text
                    except AttributeError:
                        premis_event['%s' % (key)] = ''

                # create list of dicts for PREMIS Agents associated with Events
                premis_agents = []

                # parse info for each PREMIS Agent pair associated with Event
                premis_event_xpath = ".//xmlData/event/linkingAgentIdentifier"
                for target3 in target2.findall(premis_event_xpath):

                    premis_agent = {}
                    try:
                        premis_agent['agent_type'] = target3.find('./linkingAgentIdentifierType').text
                    except AttributeError:
                        premis_agent['agent_type'] = ''

                    try:
                        premis_agent['agent_value'] = target3.find('./linkingAgentIdentifierValue').text
                    except AttributeError:
                        premis_agent['agent_value'] = ''

                    # append PREMIS Agent pair to premis_agents
                    premis_agents.append(premis_agent)

                # write premis_agents to premis_events
                premis_event['premis_agents'] = premis_agents

                # write premis_event dict to file_data
                file_data['premis_events'].append(premis_event)

        # format filepath
        file_data['filepath'] = file_data['filepath'].replace('%transferDirectory%', '')

        # format PUID
        file_data['puid'] = "%s" % (file_data['puid'], file_data['puid'])

        # create human-readable size
        file_data['size'] = convert_size(file_data['bytes'])

        # add file_data to list
        original_files.append(file_data)

    mets_instance = METS('%s' % (mets_filename), original_files)
    db.session.add(mets_instance)
    db.session.commit()

@app.route("/", methods=['GET', 'POST'])
def index():
    return render_template('upload.html', )

@app.route('/originalfiles', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return render_template('upload.html')
        file = request.files['file']
        # if user does not select file, browser also
        # submit a empty part without filename
        if file.filename == '':
            flash('No selected file')
            return render_template('upload.html')
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            mets_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
            global aip_name
            aip_name = os.path.basename(filename)
            global original_files
            original_files = mets_to_list_of_dicts(mets_path) # write details to global original_files list of dicts
            os.remove(os.path.join(app.config['UPLOAD_FOLDER'], filename)) # delte file from uploads folder
            return render_template('originalfiles.html', original_files = original_files, aip_name = aip_name)

@app.route('/file/<UUID>')
def show_file(UUID):
    for original_file in original_files:
        if original_file["uuid"] == UUID:
            target_original_file = original_file
            break
    return render_template('detail.html', original_file=target_original_file, aip_name = aip_name)

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

<|endoftext|>


<commit_before>import logging

from PyQt5.QtWidgets import QDialog, QPushButton, QWidget
from typing import Optional, Callable, Dict

from sas.system.config.config import config
from sas.qtgui.Utilities.Preferences.UI.PreferencesUI import Ui_preferencesUI
from sas.qtgui.Utilities.Preferences.PreferencesWidget import PreferencesWidget

# PreferenceWidget Imports go here and then are added to the BASE_PANELS, but not instantiated.
# The PreferencesPanel object will instantiate all widgets during its instantiation.
#  e.g:
#  `from foo.bar import BarWidget  # BarWidget is a child of PreferencesWidget`
#  `BASE_PANELS = {"Bar Widget Options": BarWidget}`

# Pre-made option widgets
BASE_PANELS = {}  # type: Dict[str, PreferencesWidget]

logger = logging.getLogger(__name__)

class PreferencesPanel(QDialog, Ui_preferencesUI):
    """A preferences panel to house all SasView related settings. The left side of the window is a listWidget with a
    options menus available. The right side of the window is a stackedWidget object that houses the options
    associated with each listWidget item.
    **Important Note** When adding new preference widgets, the index for the listWidget and stackedWidget *must* match
    Release notes:
    SasView v5.0.5: Added defaults for loaded data units and plotted units
    """

    def __init__(self, parent=None):
        super(PreferencesPanel, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.setWindowTitle("Preferences")
        self.warning = None
        # A list of callables used to restore the default values for each item in StackedWidget
        self.restoreDefaultMethods = []
        # Add predefined widgets to window
        self.addWidgets(BASE_PANELS)
        # Set defaults values for the list and stacked widgets
        self.stackedWidget.setCurrentIndex(0)
        self.listWidget.setCurrentRow(0)
        # Add window actions
        self.listWidget.currentItemChanged.connect(self.prefMenuChanged)
        self.buttonBox.clicked.connect(self.onClick)

    def addWidgets(self,widgets: Dict[str, Callable]):
        """Add a list of widgets to the window"""
        for name, widget in widgets.items():
            if isinstance(widget, PreferencesWidget):
                self.addWidget(widget)
            else:
                self.addWidget(widget())

    def prefMenuChanged(self):
        """When the preferences menu selection changes, change to the appropriate preferences widget """
        row = self.listWidget.currentRow()
        self.stackedWidget.setCurrentIndex(row)

    def onClick(self, btn: QPushButton):
        """Handle button click events in one area"""
        # Reset to the default preferences
        if btn.text() == 'Restore Defaults':
            self.restoreDefaultPreferences()
        elif btn.text() == 'OK':
            self.close()
        elif btn.text() == 'Help':
            self.help()

    def restoreDefaultPreferences(self):
        """Reset all preferences to their default preferences"""
        for method in self.restoreDefaultMethods:
            if callable(method):
                method()
            else:
                logger.warning(f'While restoring defaults, {str(method)} of type {type(method)}'
                               + ' was given. A callable object was expected.')

    def close(self):
        """Save the configuration values when the preferences window is closed"""
        config.save()
        super(PreferencesPanel, self).close()

    def addWidget(self, widget: QWidget, name: Optional[str] = None):
        self.stackedWidget.addWidget(widget)
        name = widget.name if hasattr(widget, 'name') and widget.name else name
        name = "Unknown" if not name else name
        self.listWidget.addItem(name)
        if hasattr(widget, 'resetDefaults') and callable(widget.resetDefaults):
            self.restoreDefaultMethods.append(widget.resetDefaults)

    def help(self):
        """Open the help window associated with the preferences window"""
        tree_location = "/user/qtgui/MainWindow/preferences_help.html"
        self.parent.showHelp(tree_location)
<commit_msg>Document the API for adding a new widget and give an outline of what that widget requires<commit_after>import logging

from PyQt5.QtWidgets import QDialog, QPushButton, QWidget
from typing import Optional, Callable, Dict, Type

from sas.system.config.config import config
from sas.qtgui.Utilities.Preferences.UI.PreferencesUI import Ui_preferencesUI
from sas.qtgui.Utilities.Preferences.PreferencesWidget import PreferencesWidget

# The PreferencesPanel object will instantiate all widgets during its instantiation.
#  e.g:
#  `from foo.bar import BarWidget  # BarWidget is a child of PreferencesWidget`
#  `BASE_PANELS = {"Bar Widget Options": BarWidget}`
# PreferenceWidget Imports go here and then are added to the BASE_PANELS, but not instantiated.

# Pre-made option widgets
BASE_PANELS = {}  # type: Dict[str, Type[PreferencesWidget]]

logger = logging.getLogger(__name__)

class PreferencesPanel(QDialog, Ui_preferencesUI):
    """A preferences panel to house all SasView related settings. The left side of the window is a listWidget with a
    options menus available. The right side of the window is a stackedWidget object that houses the options
    associated with each listWidget item.
    **Important Note** When adding new preference widgets, the index for the listWidget and stackedWidget *must* match
    Release notes:
    SasView v5.0.5: Added defaults for loaded data units and plotted units
    """

    def __init__(self, parent=None):
        super(PreferencesPanel, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.setWindowTitle("Preferences")
        self.warning = None
        # A list of callables used to restore the default values for each item in StackedWidget
        self.restoreDefaultMethods = []
        # Add predefined widgets to window
        self.addWidgets(BASE_PANELS)
        # Set defaults values for the list and stacked widgets
        self.stackedWidget.setCurrentIndex(0)
        self.listWidget.setCurrentRow(0)
        # Add window actions
        self.listWidget.currentItemChanged.connect(self.prefMenuChanged)
        self.buttonBox.clicked.connect(self.onClick)

    def addWidgets(self, widgets: Dict[str, Callable]):
        """Add a list of widgets to the window"""
        for name, widget in widgets.items():
            if isinstance(widget, PreferencesWidget):
                self.addWidget(widget, name)
            elif callable(widget):
                self.addWidget(widget(), name)

    def prefMenuChanged(self):
        """When the preferences menu selection changes, change to the appropriate preferences widget """
        row = self.listWidget.currentRow()
        self.stackedWidget.setCurrentIndex(row)

    def onClick(self, btn: QPushButton):
        """Handle button click events in one area"""
        # Reset to the default preferences
        if btn.text() == 'Restore Defaults':
            self.restoreDefaultPreferences()
        elif btn.text() == 'OK':
            self.close()
        elif btn.text() == 'Help':
            self.help()

    def restoreDefaultPreferences(self):
        """Reset all preferences to their default preferences"""
        for method in self.restoreDefaultMethods:
            if callable(method):
                method()
            else:
                logger.warning(f'While restoring defaults, {str(method)} of type {type(method)}'
                               + ' was given. A callable object was expected.')

    def close(self):
        """Save the configuration values when the preferences window is closed"""
        config.save()
        super(PreferencesPanel, self).close()

    def addWidget(self, widget: QWidget, name: Optional[str] = None):
        self.stackedWidget.addWidget(widget)
        name = widget.name if hasattr(widget, 'name') and widget.name else name
        name = "Unknown" if not name else name
        self.listWidget.addItem(name)
        if hasattr(widget, 'resetDefaults') and callable(widget.resetDefaults):
            self.restoreDefaultMethods.append(widget.resetDefaults)

    def help(self):
        """Open the help window associated with the preferences window"""
        tree_location = "/user/qtgui/MainWindow/preferences_help.html"
        self.parent.showHelp(tree_location)
<|endoftext|>