Created
October 9, 2012 18:08
-
-
Save wpjunior/3860415 to your computer and use it in GitHub Desktop.
Prova/models.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # -*- coding: utf-8 -*- | |
| # | |
| # Copyright (C) 2012 Wilson Pinto Júnior <[email protected]> | |
| # | |
| # This program is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation, either version 3 of the License, or | |
| # (at your option) any later version. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| import datetime | |
| from bson import ObjectId | |
| from django.http import HttpResponse | |
| from django.core.servers.basehttp import FileWrapper | |
| from bson import ObjectId | |
| from mongoengine import * | |
| from mongoengine.queryset import Q, QuerySet | |
| from eleicoes.accounts.models import Eleitor | |
| from eleicoes.unidades.models import Unidade, Turma, Serie | |
| from constants import * | |
| class Opcao(EmbeddedDocument): | |
| _id = ObjectIdField(default=ObjectId) | |
| nome = StringField(max_length=200) | |
| correct = BooleanField(default=False) | |
| meta = {'allow_inheritance': False} | |
| @property | |
| def pk(self): | |
| return self._id | |
| @classmethod | |
| def load(cls, data): | |
| """ | |
| Carrega a questão vindo de um arquivo JSON | |
| """ | |
| obj = cls() | |
| _id = data.get('id', None) | |
| if _id: | |
| obj._id = ObjectId(_id) | |
| obj.nome = data.get('nome') | |
| obj.correct = data.get('correct') | |
| return obj | |
| def to_json(self): | |
| return { | |
| 'id': str(self._id), | |
| 'nome': self.nome, | |
| 'correct': self.correct | |
| } | |
| class Questao(EmbeddedDocument): | |
| _id = ObjectIdField(default=ObjectId) | |
| questao = StringField() | |
| tp = StringField( | |
| verbose_name=u"Tipo de questão", | |
| choices=QUESTAO_TP_CHOICES) | |
| opcoes = ListField(EmbeddedDocumentField(Opcao)) | |
| pontos = IntField() | |
| meta = {'allow_inheritance': False} | |
| @classmethod | |
| def load(cls, data): | |
| """ | |
| Carrega a questão vindo de um arquivo JSON | |
| """ | |
| obj = cls() | |
| if data.has_key('id'): | |
| obj._id = ObjectId(data['id']) | |
| obj.tp = data.get('tp') | |
| obj.questao = data.get('questao') | |
| obj.pontos = data.get('pontos') | |
| if data.has_key('opcoes'): | |
| obj.opcoes = [Opcao.load(d) for d in data['opcoes']] | |
| else: | |
| obj.opcoes = [] | |
| return obj | |
| def to_json(self): | |
| o = { | |
| 'id': str(self._id), | |
| 'questao': self.questao, | |
| 'tp': self.tp, | |
| 'opcoes': [o.to_json() for o in self.opcoes], | |
| 'pontos': self.pontos | |
| } | |
| return o | |
| class ProvaQuerySet(QuerySet): | |
| def open_for_eleitor(self, user): | |
| """ | |
| Retorna as provas disponíveis para o eleitor | |
| """ | |
| self.filter( | |
| (Q(serie=user.serie, turma=None, unidade=None)| # Restrito a série do eleitor | |
| Q(serie=user.serie, turma=None, unidade=user.unidade)| # quando uma prova é amarrada pela série/unidade | |
| Q(serie=None, turma=None, unidade=None)| # quando uma prova é pública a todos eleitores | |
| Q(serie=None, turma=None, unidade=user.unidade)| # quando uma prova é para apenas uma unidade | |
| Q(turma=user.turma)), # quando uma prova é amarrada pela turma | |
| status='a') | |
| return self | |
| class Prova(Document): | |
| nome = StringField(required=True, max_length=150) | |
| questoes = ListField(EmbeddedDocumentField(Questao)) | |
| status = StringField( | |
| required=True, | |
| verbose_name="Status", | |
| choices=PROVA_STATUS_CHOICES) | |
| unidade = ReferenceField( | |
| Unidade, verbose_name="Unidade") | |
| serie = ReferenceField( | |
| Serie, verbose_name=u"Série") | |
| turma = ReferenceField( | |
| Turma, verbose_name="Turma") | |
| categoria = StringField( | |
| required=False, | |
| verbose_name="Categoria", | |
| max_length=200) | |
| pontuacao_maxima = IntField() | |
| meta = {'allow_inheritance': False, | |
| 'queryset_class': ProvaQuerySet} | |
| def get_resultados_url(self): | |
| return "/provas/resultados/%s/" % self.pk | |
| def load(self, data): | |
| """ | |
| Carrega informações atravez de um JSON | |
| """ | |
| self.nome = data.get('nome') | |
| self.status = data.get('status') | |
| self.categoria = data.get('categoria') | |
| if data.get('serie', None): | |
| self.serie = Serie.objects.with_id(data['serie']) | |
| else: | |
| self.serie = None | |
| if data.get('unidade', None): | |
| self.unidade = Unidade.objects.with_id(data['unidade']) | |
| else: | |
| self.unidade = None | |
| if data.get('turma', None): | |
| self.turma = Turma.objects.with_id(data['turma']) | |
| else: | |
| self.turma = None | |
| if data.has_key('questoes'): | |
| self.questoes = [Questao.load(d) for d in data['questoes']] | |
| else: | |
| self.questoes = [] | |
| def save(self, *args, **kwargs): | |
| self.calculate_pontuacao_maxima() | |
| return super(Prova, self).save(*args, **kwargs) | |
| def calculate_pontuacao_maxima(self): | |
| self.pontuacao_maxima = sum([ | |
| q.pontos for q in self.questoes]) | |
| def get_absolute_url(self): | |
| return '/provas/%s/' % self.pk | |
| def get_responder_url(self): | |
| return '/provas/responder/%s/' % self.pk | |
| def get_resultado_url(self): | |
| return '/provas/resultado/%s/' % self.pk | |
| def pontos_list_sorted_by_name(self): | |
| lista = [ | |
| ("/provas/resposta/%s" % pk, eleitor.get_full_name(), pontos) | |
| for pk, eleitor, pontos in Resposta.objects( | |
| prova=self).scalar('pk', 'eleitor', 'pontuacao')] | |
| return sorted(lista, key=lambda x: x[1]) | |
| def pontos_list_sorted_by_pontos(self): | |
| return list([ | |
| ("/provas/resposta/%s" % pk, eleitor.get_full_name(), pontos) | |
| for pk, eleitor, pontos in Resposta.objects( | |
| prova=self).order_by('-pontuacao').scalar('pk', 'eleitor', 'pontuacao')]) | |
| def to_json(self): | |
| o = { | |
| 'nome': self.nome, | |
| 'status': self.status} | |
| if self.categoria: | |
| o['categoria'] = self.categoria | |
| if self.questoes: | |
| o['questoes'] = [q.to_json() for q in self.questoes] | |
| if self.serie: | |
| o['serie'] = str(self.serie.pk) | |
| if self.unidade: | |
| o['unidade'] = str(self.unidade.pk) | |
| if self.turma: | |
| o['turma'] = str(self.turma.pk) | |
| return o | |
| def resultado_by_unidade(self): | |
| for unidade in Resposta.objects(prova=self).distinct('unidade'): | |
| yield ResultadoUnidade(unidade=unidade, prova=self) | |
| @property | |
| def resultado_pontuacao_maxima(self): | |
| max_obj = Resposta.objects( | |
| prova=self).order_by( | |
| '-pontuacao').first() | |
| if max_obj: | |
| return max_obj.pontuacao | |
| else: | |
| return 0 | |
| @property | |
| def resultado_pontuacao_minima(self): | |
| min_obj = Resposta.objects( | |
| prova=self).order_by( | |
| 'pontuacao').first() | |
| if min_obj: | |
| return min_obj.pontuacao | |
| else: | |
| return 0 | |
| @property | |
| def resultado_pontuacao_media(self): | |
| avg = Resposta.objects( | |
| prova=self).average('pontuacao') | |
| return "%0.4f" % avg | |
| @property | |
| def resultado_total_alunos(self): | |
| return Eleitor.objects(tp="A", is_active=True).count() | |
| @property | |
| def resultado_total_provas(self): | |
| return Resposta.objects(prova=self).count() | |
| class QuestaoResposta(EmbeddedDocument): | |
| _id = ObjectIdField() | |
| correct = BooleanField() | |
| opcao = ObjectIdField() | |
| opcoes = ListField( | |
| ObjectIdField()) | |
| meta = {'allow_inheritance': False} | |
| @property | |
| def pk(self): | |
| return str(self._id) | |
| class RespostaQuerySet(QuerySet): | |
| def get_unidade_avg(self): | |
| """ | |
| Retorna o mapa total da média da prova | |
| """ | |
| map_func = """ | |
| function() { | |
| if (this.unidade) | |
| var key = this.unidade.$id.toString(); | |
| else | |
| var key = null; | |
| var out = { | |
| pontuacao: this.pontuacao, | |
| count: 1, avg: 0.0, | |
| series: {}} | |
| if (this.serie) { | |
| var serieOut = { | |
| pontuacao: this.pontuacao, count: 1, avg: 0.0, turmas: {}}; | |
| if (this.turma) { | |
| serieOut.turmas[this.turma.$id.toString()] = { | |
| pontuacao: this.pontuacao, count: 1, avg: 0.0} | |
| } | |
| out.series[this.serie.$id.toString()] = serieOut; | |
| } | |
| emit(key, out); | |
| } | |
| """ | |
| reduce_func = """ | |
| function(key, values) { | |
| var out = {count: 0, pontuacao: 0.0, avg: 0.0, series: {}}; | |
| values.forEach(function (value) { | |
| out.count += value.count; | |
| out.pontuacao += value.pontuacao; | |
| if (value.series) { | |
| for (var serie in value.series) { | |
| if (!out.series[serie]) | |
| out.series[serie] = {pontuacao: 0, count: 0, avg: 0.0, turmas: {}}; | |
| out.series[serie].count += value.series[serie].count; | |
| out.series[serie].pontuacao += value.series[serie].pontuacao; | |
| if (value.series[serie].turmas) { | |
| for (var turma in value.series[serie].turmas) { | |
| if (!out.series[serie].turmas[turma]) | |
| out.series[serie].turmas[turma] = {pontuacao: 0, count: 0, avg: 0.0}; | |
| out.series[serie].turmas[turma].count += value.series[serie].turmas[turma].count; | |
| out.series[serie].turmas[turma].pontuacao += value.series[serie].turmas[turma].pontuacao; | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| return out; | |
| } | |
| """ | |
| finalize_func = """ | |
| function (key, value) { | |
| if (value.count > 0) | |
| value.avg = value.pontuacao / value.count; | |
| if (value.series) { | |
| for (var serie in value.series) { | |
| value.series[serie].avg = value.series[serie].pontuacao / value.series[serie].count; | |
| if (value.series[serie].turmas) { | |
| for (var turma in value.series[serie].turmas) { | |
| value.series[serie].turmas[turma].avg = ( | |
| value.series[serie].turmas[turma].pontuacao / value.series[serie].turmas[turma].count) | |
| } | |
| } | |
| } | |
| } | |
| return value; | |
| } | |
| """ | |
| values = self.map_reduce( | |
| map_f=map_func, reduce_f=reduce_func, | |
| output='inline', finalize_f=finalize_func) | |
| _map = {} | |
| for v in values: | |
| _map[v.key] = v.value | |
| serie_map = dict([ | |
| (str(pk), nome) | |
| for pk, nome in Serie.objects.scalar('pk', 'nome')]) | |
| for pk, unidade in Unidade.objects.in_bulk([ | |
| ObjectId(u) for u in _map.iterkeys() if u]).iteritems(): | |
| unidade_data = _map[str(pk)] | |
| unidade_data['nome'] = unidade.nome | |
| unidade_data['pk'] = str(pk) | |
| for serie_pk, serie in unidade_data['series'].iteritems(): | |
| serie_data = unidade_data['series'][str(serie_pk)] | |
| serie_data['pk'] = str(serie_pk) | |
| serie_data['nome'] = serie_map.get(serie_pk) | |
| for turma_pk, turma in Turma.objects.in_bulk([ | |
| ObjectId(t) for t in serie_data['turmas'].iterkeys() if t]).iteritems(): | |
| turma_data = serie_data['turmas'][str(turma_pk)] | |
| turma_data['pk'] = str(turma_pk) | |
| turma_data['nome'] = turma.nome | |
| serie_data['turmas'] = serie_data['turmas'].values() | |
| unidade_data['series'] = unidade_data['series'].values() | |
| return _map.values() | |
| class Resposta(Document): | |
| eleitor = ReferenceField( | |
| 'Eleitor', required=True, dbref=True) | |
| serie = ReferenceField('Serie', dbref=True) | |
| unidade = ReferenceField('Unidade', dbref=True) | |
| turma = ReferenceField('Turma', dbref=True) | |
| prova = ReferenceField( | |
| Prova, required=True, dbref=True) | |
| pontuacao = IntField() | |
| resp = ListField(EmbeddedDocumentField( | |
| QuestaoResposta)) | |
| meta = {'allow_inheritance': False, | |
| 'queryset_class': RespostaQuerySet} | |
| def scalar_questoes(self): | |
| resp_questoes_map = dict([ | |
| (str(r._id), r) | |
| for r in self.resp]) | |
| for questao in self.prova.questoes: | |
| yield QuestaoAdapter(resp_questoes_map.get(str(questao._id)), | |
| questao) | |
| def get_absolute_url(self): | |
| return "/provas/resposta/%s/" % self.pk | |
| @classmethod | |
| def for_eleitor(cls, user): | |
| respostas = [] | |
| for prova in Prova.objects(status='l'): | |
| for resposta in cls.objects(prova=prova, eleitor=user): | |
| resposta.append(resposta) | |
| return respostas | |
| PHOTO_MIMETYPES = { | |
| 'PNG': 'image/png', | |
| 'JPEG': 'image/jpeg', | |
| 'JPG': 'image/jpeg', | |
| #TODO: se encontrar mais coloque aqui | |
| } | |
| class ProvaPhoto(Document): | |
| image = ImageField( | |
| db_alias='fs', #database especial para arquivos | |
| size=(800, 600), | |
| thumbnail_size=(320, 240, False)) | |
| created = DateTimeField(default=datetime.datetime.now) | |
| def get_url(self): | |
| return u"/provas/photos/%s.jpg" % self.pk | |
| def get_thumb_url(self): | |
| return u"/provas/photos/%s.thumb.jpg" % self.pk | |
| @property | |
| def mimetype(self): | |
| return PHOTO_MIMETYPES.get(self.image.format) | |
| def as_response(self): | |
| wrapper = FileWrapper(self.image) | |
| response = HttpResponse(wrapper, content_type=self.mimetype) | |
| response['Cache-Control'] = 'no-store, no-cache, must-revalidate' | |
| response['Expires'] = 'Sat, 26 Jul 1997 05:00:00 GMT' | |
| response['Pragma'] = 'no-cache' | |
| return response | |
| def json_format(self): | |
| return { | |
| 'pk': str(self.pk), | |
| 'url': self.get_url(), | |
| 'thumb_url': self.get_thumb_url() | |
| } | |
| def as_thumb_response(self): | |
| wrapper = FileWrapper(self.image.thumbnail) | |
| response = HttpResponse(wrapper, content_type=self.mimetype) | |
| response['Cache-Control'] = 'no-store, no-cache, must-revalidate' | |
| response['Expires'] = 'Sat, 26 Jul 1997 05:00:00 GMT' | |
| response['Pragma'] = 'no-cache' | |
| return response | |
| def delete(self, force=False, *args, **kwargs): | |
| self.image.delete() #apaga a imagem do banco de dados | |
| return super(ProvaPhoto, self).delete(*args, **kwargs) | |
| meta = {'allow_inheritance': False, | |
| 'collection': 'prova_photo', | |
| 'ordering': ['created'], | |
| 'indexes': [ | |
| {'fields': ['created']}, | |
| ]} | |
| class ResultadoUnidade(object): | |
| def __init__(self, unidade, prova): | |
| self.unidade = unidade | |
| self.prova = prova | |
| @property | |
| def total_alunos(self): | |
| return Eleitor.objects( | |
| tp="A", is_active=True, | |
| unidade=self.unidade).count() | |
| @property | |
| def total_provas(self): | |
| return Resposta.objects( | |
| unidade=self.unidade, prova=self.prova).count() | |
| @property | |
| def pontuacao_maxima(self): | |
| max_obj = Resposta.objects( | |
| unidade=self.unidade, prova=self.prova).order_by( | |
| '-pontuacao').first() | |
| if max_obj: | |
| return max_obj.pontuacao | |
| else: | |
| return 0 | |
| @property | |
| def pontuacao_minima(self): | |
| min_obj = Resposta.objects( | |
| unidade=self.unidade, prova=self.prova).order_by( | |
| 'pontuacao').first() | |
| if min_obj: | |
| return min_obj.pontuacao | |
| else: | |
| return 0 | |
| @property | |
| def pontuacao_media(self): | |
| avg = Resposta.objects( | |
| unidade=self.unidade, prova=self.prova).average('pontuacao') | |
| return "%0.4f" % avg | |
| def resultado_by_serie(self): | |
| for serie in Resposta.objects( | |
| prova=self.prova, unidade=self.unidade).distinct('serie'): | |
| yield ResultadoSerie( | |
| prova=self.prova, | |
| unidade=self.unidade, | |
| serie=serie) | |
| class ResultadoSerie(object): | |
| def __init__(self, prova, unidade, serie): | |
| self.prova = prova | |
| self.unidade = unidade | |
| self.serie = serie | |
| @property | |
| def total_alunos(self): | |
| return Eleitor.objects( | |
| tp="A", unidade=self.unidade, | |
| is_active=True, | |
| serie=self.serie).count() | |
| @property | |
| def total_provas(self): | |
| return Resposta.objects( | |
| unidade=self.unidade, | |
| serie=self.serie, prova=self.prova).count() | |
| @property | |
| def pontuacao_maxima(self): | |
| max_obj = Resposta.objects( | |
| unidade=self.unidade, | |
| prova=self.prova, | |
| serie=self.serie).order_by( | |
| '-pontuacao').first() | |
| if max_obj: | |
| return max_obj.pontuacao | |
| else: | |
| return 0 | |
| @property | |
| def pontuacao_minima(self): | |
| min_obj = Resposta.objects( | |
| unidade=self.unidade, | |
| prova=self.prova, | |
| serie=self.serie).order_by( | |
| 'pontuacao').first() | |
| if min_obj: | |
| return min_obj.pontuacao | |
| else: | |
| return 0 | |
| @property | |
| def pontuacao_media(self): | |
| avg = Resposta.objects( | |
| unidade=self.unidade, | |
| prova=self.prova, | |
| serie=self.serie).average('pontuacao') | |
| return "%0.4f" % avg | |
| def resultado_by_turma(self): | |
| for turma in Resposta.objects( | |
| prova=self.prova, unidade=self.unidade, | |
| serie=self.serie).distinct('turma'): | |
| yield ResultadoTurma( | |
| prova=self.prova, | |
| unidade=self.unidade, | |
| serie=self.serie, | |
| turma=turma) | |
| class ResultadoTurma(object): | |
| def __init__(self, prova, serie, unidade, turma): | |
| self.prova = prova | |
| self.serie = serie | |
| self.unidade = unidade | |
| self.turma = turma | |
| @property | |
| def total_alunos(self): | |
| return Eleitor.objects( | |
| tp="A", unidade=self.unidade, | |
| is_active=True, | |
| turma=self.turma).count() | |
| @property | |
| def total_provas(self): | |
| return Resposta.objects( | |
| unidade=self.unidade, | |
| turma=self.turma, | |
| prova=self.prova).count() | |
| @property | |
| def pontuacao_maxima(self): | |
| max_obj = Resposta.objects( | |
| prova=self.prova, | |
| turma=self.turma).order_by( | |
| '-pontuacao').first() | |
| if max_obj: | |
| return max_obj.pontuacao | |
| else: | |
| return 0 | |
| @property | |
| def pontuacao_minima(self): | |
| min_obj = Resposta.objects( | |
| prova=self.prova, | |
| turma=self.turma).order_by( | |
| 'pontuacao').first() | |
| if min_obj: | |
| return min_obj.pontuacao | |
| else: | |
| return 0 | |
| @property | |
| def pontuacao_media(self): | |
| avg = Resposta.objects( | |
| prova=self.prova, | |
| turma=self.turma).average('pontuacao') | |
| return "%0.4f" % avg | |
| class QuestaoAdapter(object): | |
| def __init__(self, questao_resposta, questao): | |
| self.questao_resposta = questao_resposta | |
| self.questao = questao | |
| @property | |
| def nome(self): | |
| return self.questao.questao | |
| @property | |
| def pontos(self): | |
| return self.questao.pontos | |
| @property | |
| def opcoes(self): | |
| return self.questao.opcoes | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment