diff --git a/administration/static/js/edit-class/app.js b/administration/static/js/edit-class/app.js index e488cf23d..c3123d984 100644 --- a/administration/static/js/edit-class/app.js +++ b/administration/static/js/edit-class/app.js @@ -7,7 +7,10 @@ 'directive.waiting-screen', 'directive.alertPopup', 'directive.fixedBar', + 'ngSanitize', 'timtec-models', 'ui.bootstrap', + 'ui.select', + 'header', ]); })(angular); diff --git a/administration/static/js/edit-class/controllers.js b/administration/static/js/edit-class/controllers.js index 00e47ecc5..d317ec387 100644 --- a/administration/static/js/edit-class/controllers.js +++ b/administration/static/js/edit-class/controllers.js @@ -33,24 +33,25 @@ $scope.remove_item = function(index){ // removing from 'screen' list - var student_id = $scope.classe.students[index].user.id; + var student_id = $scope.classe.students_details[index].user.id; $scope.classe.students.splice(index, 1); // remove from real list - var index_management = $scope.classe.students_management.indexOf(student_id); - $scope.classe.students_management.splice(index_management, 1); + var index_management = $scope.classe.students.indexOf(student_id); + $scope.classe.students.splice(index_management, 1); + $scope.save(); }; $scope.on_select_student = function(model) { - $scope.classe.students.unshift(model); - $scope.classe.students_management.unshift(model.id); + $scope.classe.students_details.unshift(model); + $scope.classe.students.unshift(model.id); $scope.asyncSelected = ''; $scope.save(); }; $scope.toggle_certificate = function (index){ - var student = $scope.classe.students[index]; + var student = $scope.classe.students_details[index]; if(student.certificate) { var cc_id = student.certificate.link_hash; var user_id = student.user.id; @@ -68,9 +69,6 @@ $scope.save = function(){ $scope.classe.$resolved = false; - if($scope.classe.assistant) { - $scope.classe.assistant_management = $scope.classe.assistant.id; - } $scope.classe.$update() .then(function(){ diff --git a/core/admin.py b/core/admin.py index 37bc67eb3..6aa47b466 100644 --- a/core/admin.py +++ b/core/admin.py @@ -44,8 +44,8 @@ class VideoAdmin(ModelAdmin): class ClassAdmin(ModelAdmin): - search_fields = ('name', 'course', 'assistant') - list_display = ('name', 'assistant', 'course') + search_fields = ('name', 'course', 'assistants') + list_display = ('name', 'course') filter_horizontal = ('students', ) @@ -64,6 +64,11 @@ class CertficateAdmin(ModelAdmin): # list_display = ('course_student__user__username', 'course_student__course__name',) +class CertficateTemplateAdmin(ModelAdmin): + pass + # list_display = ('course_student__user__username', 'course_student__course__name',) + + admin.site.register(Video, VideoAdmin) admin.site.register(CourseProfessor, CourseProfessorAdmin) admin.site.register(Course, CourseAdmin) @@ -74,5 +79,6 @@ class CertficateAdmin(ModelAdmin): admin.site.register(ProfessorMessage) admin.site.register(Class, ClassAdmin) admin.site.register(CourseCertification, CertficateAdmin) +admin.site.register(CertificateTemplate, CertficateTemplateAdmin) admin.site.register(CertificationProcess) admin.site.register(Evaluation) diff --git a/core/forms.py b/core/forms.py index baae0a953..d8736afae 100644 --- a/core/forms.py +++ b/core/forms.py @@ -43,3 +43,41 @@ def save(self, commit=True): uid = self.cleaned_data['user_id'] self.instance.remove_students(get_user_model().objects.get(id=uid)) return super(RemoveStudentForm, self).save(commit=commit) + + +class AddStudentsForm(forms.ModelForm): + class Meta: + model = Class + fields = [] + + students_text = forms.CharField() + + def clean_students_text(self): + data = self.cleaned_data['students_text'].strip().split() + data = [u.strip() for u in data] + return data + + def save(self, commit=True): + User = get_user_model() + students = self.cleaned_data['students_text'] + + for student_name in students: + try: + student = User.objects.get( + Q(username=student_name) | + Q(email=student_name) + ) + self.instance.add_students(student) + + if self.data.get('auto_enroll', False) == "True": + # Check if the new user is already enrolled + try: + CourseStudent.objects.get(user=student, course=self.instance.course) + except CourseStudent.DoesNotExist: + CourseStudent.objects.create(user=student, course=self.instance.course) + pass + + except User.DoesNotExist: + logger.info(u'student with username: %s does not exist' % student_name) + + return super(AddStudentsForm, self).save(commit=commit) diff --git a/core/migrations/0020_merge.py b/core/migrations/0020_merge.py new file mode 100644 index 000000000..29eae66c1 --- /dev/null +++ b/core/migrations/0020_merge.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0017_coursestudent_start_date'), + ('core', '0019_auto_20161014_2116'), + ] + + operations = [ + ] diff --git a/core/migrations/0021_class_assistants.py b/core/migrations/0021_class_assistants.py new file mode 100644 index 000000000..408c3265e --- /dev/null +++ b/core/migrations/0021_class_assistants.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0020_merge'), + ] + + operations = [ + migrations.AddField( + model_name='class', + name='assistants', + field=models.ManyToManyField(related_name='professor_classes', verbose_name='Assistants', to=settings.AUTH_USER_MODEL, blank=True), + ), + ] diff --git a/core/migrations/0022_auto_20161125_1116.py b/core/migrations/0022_auto_20161125_1116.py new file mode 100644 index 000000000..b3ced9036 --- /dev/null +++ b/core/migrations/0022_auto_20161125_1116.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +def migrate_assistants(apps, schema_editor): + Class = apps.get_model("core", "Class") + for class_group in Class.objects.all(): + if class_group.assistant: + class_group.assistants.add(class_group.assistant) + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0021_class_assistants'), + ] + + operations = [ + migrations.RunPython(migrate_assistants), + ] diff --git a/core/migrations/0023_remove_class_assistant.py b/core/migrations/0023_remove_class_assistant.py new file mode 100644 index 000000000..65295c2d3 --- /dev/null +++ b/core/migrations/0023_remove_class_assistant.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0022_auto_20161125_1116'), + ] + + operations = [ + migrations.RemoveField( + model_name='class', + name='assistant', + ), + ] diff --git a/core/migrations/0024_certificatetemplate_signature.py b/core/migrations/0024_certificatetemplate_signature.py new file mode 100644 index 000000000..815dce77f --- /dev/null +++ b/core/migrations/0024_certificatetemplate_signature.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import core.utils + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0023_remove_class_assistant'), + ] + + operations = [ + migrations.AddField( + model_name='certificatetemplate', + name='signature', + field=models.ImageField(upload_to=core.utils.HashName(b'signature', b'organization_name'), null=True, verbose_name='Signature', blank=True), + ), + ] diff --git a/core/models.py b/core/models.py index 9c8cdb1df..e23cc9100 100644 --- a/core/models.py +++ b/core/models.py @@ -43,10 +43,10 @@ def __unicode__(self): class Class(models.Model): name = models.CharField(max_length=255) - assistant = models.ForeignKey(settings.AUTH_USER_MODEL, - verbose_name=_('Assistant'), - related_name='professor_classes', null=True, - blank=True) + assistants = models.ManyToManyField(settings.AUTH_USER_MODEL, + verbose_name=_('Assistants'), + related_name='professor_classes', + blank=True) students = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='classes', blank=True) course = models.ForeignKey('Course', verbose_name=_('Course')) @@ -63,6 +63,15 @@ def __unicode__(self): def get_absolute_url(self): return reverse('class', kwargs={'pk': self.id}) + def add_students(self, *objs): + for obj in objs: + try: + c = Class.objects.get(course=self.course, students=obj) + c.students.remove(obj) + except Class.DoesNotExist: + pass + self.students.add(obj) + def remove_students(self, *objs): for obj in objs: self.students.remove(obj) @@ -497,7 +506,7 @@ def new_message(self, course, subject, message, to=[]): professor=self) def get_current_user_classes(self): - return Class.objects.filter(course=self.course, assistant=self.user) + return Class.objects.filter(course=self.course, assistants=self.user) class CourseAuthor(models.Model): @@ -806,6 +815,8 @@ class CertificateTemplate(models.Model): upload_to=hash_name('logo', 'organization_name')) base_logo = models.ImageField(_('Logo'), null=True, blank=True, upload_to=hash_name('base_logo', 'organization_name')) + signature = models.ImageField(_('Signature'), null=True, blank=True, + upload_to=hash_name('signature', 'organization_name')) organization_name = models.CharField(_('Name'), max_length=255, blank=True, null=True) class Meta: @@ -829,6 +840,12 @@ def base_logo_url(self): return self.base_logo.url return '' + @property + def signature_url(self): + if self.signature: + return self.signature.url + return '' + class IfCertificateTemplate(CertificateTemplate): pronatec_logo = models.BooleanField(_('Pronatec'), default=False) diff --git a/core/serializers.py b/core/serializers.py index f837689d4..38ee097f0 100644 --- a/core/serializers.py +++ b/core/serializers.py @@ -66,7 +66,7 @@ class BaseClassSerializer(serializers.ModelSerializer): class Meta: model = Class - fields = ("id", "name", "assistant", "user_can_certificate") + fields = ("id", "name", "assistants", "user_can_certificate") class BaseEvaluationSerializer(serializers.ModelSerializer): @@ -89,7 +89,6 @@ class Meta: class CertificationProcessSerializer(serializers.ModelSerializer): - # TODO: Verificar se de fato e read_only=True course_certification = serializers.SlugRelatedField(slug_field="link_hash", read_only=True) class Meta: @@ -100,7 +99,7 @@ class CourseCertificationSerializer(serializers.ModelSerializer): processes = BaseCertificationProcessSerializer(many=True, read_only=True) approved = BaseCertificationProcessSerializer(source='get_approved_process', read_only=True) course = serializers.SerializerMethodField() - url = serializers.Field(source='get_absolute_url') + url = serializers.ReadOnlyField(source='get_absolute_url') class Meta: model = CourseCertification @@ -133,7 +132,7 @@ class CertificateTemplateSerializer(serializers.ModelSerializer): class Meta: model = CertificateTemplate - fields = ('id', 'course', 'organization_name', 'base_logo_url', 'cert_logo_url', 'role', 'name',) + fields = ('id', 'course', 'organization_name', 'base_logo_url', 'cert_logo_url', 'role', 'name', 'signature_url', ) class IfCertificateTemplateSerializer(CertificateTemplateSerializer): @@ -141,14 +140,38 @@ class IfCertificateTemplateSerializer(CertificateTemplateSerializer): class Meta: model = IfCertificateTemplate fields = ('id', 'course', 'organization_name', 'base_logo_url', 'cert_logo_url', - 'pronatec_logo', 'mec_logo', 'role', 'name',) + 'pronatec_logo', 'mec_logo', 'role', 'name', 'signature_url',) class CertificateTemplateImageSerializer(serializers.ModelSerializer): class Meta: model = CertificateTemplate - fields = ('base_logo', 'cert_logo', ) + fields = ('base_logo', 'cert_logo', 'signature', ) + + +class ClassActivitySerializer(serializers.ModelSerializer): + activity_answers = serializers.SerializerMethodField() + + class Meta: + model = Class + fields = ['id', 'name', 'activity_answers', 'course'] + + def get_activity_answers(self, obj): + request = self.context.get("request") + activity_id = request.query_params.get('activity', None) + + try: + queryset = Answer.objects.filter( + activity=activity_id, + activity__unit__lesson__course=obj.course, + user__in=obj.students.all() + ).exclude(user=request.user) + except Answer.DoesNotExist: + return + + return AnswerSerializer( + queryset, many=True, **{'context': self.context}).data class VideoSerializer(serializers.ModelSerializer): @@ -169,6 +192,7 @@ class CourseSerializer(serializers.ModelSerializer): has_started = serializers.Field() professors = TimtecUserSerializer(source="professors", read_only=True) home_thumbnail_url = serializers.SerializerMethodField() + professors = TimtecUserSerializer(read_only=True, many=True) is_user_assistant = serializers.SerializerMethodField() is_user_coordinator = serializers.SerializerMethodField() is_assistant_or_coordinator = serializers.SerializerMethodField() @@ -180,9 +204,7 @@ class Meta: "thumbnail_url", "home_thumbnail_url", "home_position", "start_date", "home_published", "authors_names", "has_started", "min_percent_to_complete", "is_user_assistant", "is_user_coordinator", - # TODO timtec theme specific: remove "professor_name" and "professor_names" - "professor_name", "professor_names", - "is_assistant_or_coordinator", ) + "is_assistant_or_coordinator", 'professors', ) @staticmethod def get_professor_name(obj): @@ -218,6 +240,10 @@ def get_is_assistant_or_coordinator(self, obj): class CourseStudentSerializer(serializers.ModelSerializer): user = TimtecUserSerializer(read_only=True) + course_finished = serializers.BooleanField() + can_emmit_receipt = serializers.BooleanField() + percent_progress = serializers.IntegerField() + min_percent_to_complete = serializers.IntegerField() current_class = BaseClassSerializer(source='get_current_class') course = BaseCourseSerializer() @@ -227,7 +253,7 @@ class Meta: model = CourseStudent fields = ('id', 'user', 'course', 'course_finished', 'certificate', 'can_emmit_receipt', 'percent_progress', - 'current_class', 'min_percent_to_complete', 'start_date',) + 'current_class', 'min_percent_to_complete',) class CourseStudentClassSerializer(CourseStudentSerializer): @@ -241,25 +267,22 @@ class Meta: class ClassSerializer(serializers.ModelSerializer): - students = CourseStudentClassSerializer(source='get_students', many=True, read_only=True) + students_details = CourseStudentClassSerializer(source='get_students', many=True, read_only=True) processes = CertificationProcessSerializer(read_only=True, many=True) evaluations = EvaluationSerializer(read_only=True, many=True) course = CourseSerializer(read_only=True) - assistant = TimtecUserSerializer(read_only=True) - students_management = serializers.PrimaryKeyRelatedField(many=True, read_only=False, source='students') - assistant_management = serializers.PrimaryKeyRelatedField(read_only=False, source='assistant', required=False) + assistants = TimtecUserSerializer(read_only=True, many=True) class Meta: model = Class - -class UserSocialAccountSerializer(serializers.ModelSerializer): - - get_absolute_url = serializers.Field() - - class Meta: - model = UserSocialAccount - fields = ('social_media', 'nickname', 'get_absolute_url') + def update(self, instance, validated_data, **kwargs): + assistants = self.context['request'].data.get('assistants', None) + updated_class = super(ClassSerializer, self).update(instance, validated_data) + # If there are assistans to be associated with the class, do it now + for assistant in assistants: + updated_class.assistants.add(assistant['id']) + return updated_class class ProfileSerializer(TimtecUserSerializer): diff --git a/core/static/js/certificate/controllers.js b/core/static/js/certificate/controllers.js index 7943a00e4..37e8c1ab7 100644 --- a/core/static/js/certificate/controllers.js +++ b/core/static/js/certificate/controllers.js @@ -68,6 +68,9 @@ if($scope.images.base_logo){ fu.addField('base_logo', $scope.images.base_logo); } + if($scope.images.signature){ + fu.addField('signature', $scope.images.signature); + } fu.addField('course', $scope.course_id); // return a new promise that file will be uploaded diff --git a/core/views.py b/core/views.py index 26d228698..31cd0022d 100644 --- a/core/views.py +++ b/core/views.py @@ -322,6 +322,7 @@ def list(self, request, *args, **kwargs): class CourseCertificationViewSet(viewsets.ModelViewSet): + queryset = CourseCertification.objects.all() model = CourseCertification lookup_field = 'link_hash' filter_fields = ('course_student',) @@ -462,6 +463,7 @@ def update(self, request, **kwargs): class CertificateTemplateImageViewSet(viewsets.ModelViewSet): + queryset = CertificateTemplate.objects.all() model = CertificateTemplate lookup_field = 'course' serializer_class = CertificateTemplateImageSerializer @@ -611,7 +613,7 @@ def get_queryset(self): if course.has_perm_own_all_classes(user): return queryset else: - return queryset.filter(assistant=user) + return queryset.filter(assistants=user) def get_context_data(self, **kwargs): context = super(ClassListView, self).get_context_data(**kwargs) @@ -632,7 +634,7 @@ def form_valid(self, form): class CanEditClassMixin(object): def check_permission(self, klass): user = self.request.user - if not (user == klass.assistant or klass.course.has_perm_own_all_classes(user)): + if not (klass.assistants.filter(id=user.id).exists() or klass.course.has_perm_own_all_classes(user)): raise PermissionDenied def get_object(self, queryset=None): @@ -644,7 +646,7 @@ def get_object(self, queryset=None): class ClassUpdateView(LoginRequiredMixin, CanEditClassMixin, UpdateView): model = Class template_name = 'class_edit.html' - fields = ('name', 'assistant', 'user_can_certificate',) + fields = ('name', 'assistants', 'user_can_certificate',) def form_valid(self, form): if form.changed_data: @@ -814,7 +816,7 @@ def get_queryset(self): role = '' # if user is not coordinator or admin, only show his classes if not role or role == 'assistant': - queryset = queryset.filter(assistant=self.request.user) + queryset = queryset.filter(assistants=self.request.user) return queryset diff --git "a/docs/instalacao_e_configuracao/Configura\303\247\303\265es.md" "b/docs/instalacao_e_configuracao/Configura\303\247\303\265es.md" index b720508e8..549ef83c5 100644 --- "a/docs/instalacao_e_configuracao/Configura\303\247\303\265es.md" +++ "b/docs/instalacao_e_configuracao/Configura\303\247\303\265es.md" @@ -17,6 +17,16 @@ Atente para o modelo deste arquivo aqui [settings_local.py](https://github.com/h É importante que, depois de editado, você rode os comandos pós-alterações abaixo. +## Escolha o tema + +No arquivo settings_local.py, a partir da versão 4, é importante dizer a aplicação qual é o tema ativo da seguinte forma: + +``` +INSTALLED_APPS += ( + 'if_theme', +) +``` + ## Comandos importantes pós alterações Quando fizer alterações, muitas vezes é importante reiniciar os principais serviços em uso. Tenha isso em mente toda vez que fizer alterações nos parâmetros da aplicação. diff --git a/docs/instalacao_e_configuracao/timtec_deploy.md b/docs/instalacao_e_configuracao/timtec_deploy.md index 74933524b..a9c1f8ffc 100644 --- a/docs/instalacao_e_configuracao/timtec_deploy.md +++ b/docs/instalacao_e_configuracao/timtec_deploy.md @@ -1,212 +1,221 @@ -Essa é a documentação básica de instalação (deploy) timtec. Para informações sobre requisitos e dependências, veja o [README.md](https://github.com/hacklabr/timtec/blob/master/README.md). +> **ATENÇÃO 0:** Essa é a documentação básica de instalação (deploy) timtec. Para informações sobre requisitos e dependências, veja o [README.md](https://github.com/hacklabr/timtec/blob/master/README.md). + +> **ATENÇÃO 1:** execute os comandos na sequencia em que são apresentados aqui. + +> **ATENÇÃO 2:** quando o comando precisar ser executado como **root** ele virá precedido desta indicação **root@server#**. Quando o comando precisar ser executado como **usuário da aplicação** ele virá precedido desta indicação **user@server$**. + ## Prepare o servidor Você precisa de um servidor com algum dos seguintes sistemas operacionais: -* Ubuntu 14.04, 16.04 +* Ubuntu 14.04 +* Ubuntu 16.04 * Debian 8.0 -Certifique-se de ter a senha ssh deste servidor para começar o processo e de ter permissão de sudo(root) na máquina em questão. +> **ATENÇÃO 3:** É possível instalar em outras distribuições, no entanto não fizemos os testes de +deploy que recobrem esses procedimentos. Você pode fazer estes testes e construir uma documentação com base em outros servidores. Se fizer isso, mande um pullrequest para a gente que incluiremos a documentação neste repositório oficial. -## Crie o usuário da aplicação +> **ATENÇÃO 4:** Certifique-se de ter a senha ssh deste servidor para começar o processo e de ter permissão de sudo(root) na máquina em questão. -Sugerimos que o usuário usado para fazer a instalação não seja o root. Aqui, o usuário que está fazendo a instalação é o **timtec-production**. Assim, nos comandos abaixo, substitua o nome do usuário pelo que você estiver usando. Os scripts possuem uma variável para definir o usuário que executará o proxy (wsgi). Caso use um usuário diferente, olhe o arquivo Makefile e faça as alterações necessárias. +## Dependências -Este tutorial pressupões que você esteja logado com o usuário timtec-production, que ele esteja no grupo sudo. +* Atualize o **ÍNDICE DE PACOTES** de seu servidor -É possível verificar se o usuário timtec-production existe através do comando: ``` -root@server# grep timtec-production /etc/passwd +root@server# apt-get update ``` -Caso o usuário exista, o comando acima deve retornar uma linha semelhante a seguinte: -``` -timtec-production:x:999:999::/home/timtec-production: -``` -Caso não exista é possível criá-lo com: +* Instale a ferramenta **GIT** ``` -root@server# useradd --groups sudo --create-home timtec-production +root@server# apt-get install git ``` -Depois mude a senha: + +* Instale os pacotes do **PYTHON** ``` -root@server# passwd timtec-production +root@server# apt-get install build-essential python-dev gettext python-virtualenv ``` -Se você estiver usando Debian, pode acontecer do sistema criar uma instância do novo usuário com acesso a um terminal sh. Se você quiser usar o bash (terminal mais completo e com mais funcionalidades), você pode alterar essa informação no arquivo de configuração de usuários. Proceda da seguinte maneira: +* Instale algumas **BIBLIOTECAS DE PROCESSAMENTO DE IMAGENS** -1) Abra o arquivo /etc/passwd e verifique a linha onde está o usuário timtec-production. Você pode ver uma linha assim: ``` -timtec-production:x:1001:1001::/home/timtec-production:/bin/sh +root@server# apt-get install libpq-dev libjpeg-dev libpng12-dev ``` -2) Repare que a linha tem indicação para o terminal sh. Mude para bash e salve o arquivo com a linha desta maneira (use vim, nano ou qualquer editor de sua preferência): -``` -timtec-production:x:1001:1001::/home/timtec-production:/bin/bash -``` +* Instale o **NODEJS** -## Obtendo o código +> **ATENÇÃO 5:** Você pode instalar o nodejs no Debian ou no Ubuntu, mas o procedimento é diferente em cada um deles. Veja abaixo e escolha de acordo com sua distribuição. -Se você estiver usando Debian, pode acontecer do sistema criar uma instância do novo usuário com acesso a um terminal sh. Se você quiser usar o bash (terminal mais completo e com mais funcionalidades), você pode alterar essa informação no arquivo de configuração de usuários. Proceda da seguinte maneira: +* Nodejs no **Ubuntu** +Diferente de todas as outras distribuições, o ubuntu usa o comando node para o nodejs por padrão, então vamos fazer isso. -1) Abra o arquivo /etc/passwd e verifique a linha onde está o usuário timtec. Você pode ver uma linha assim: ``` - timtec:x:1001:1001::/home/timtec:/bin/sh +root@server# apt-get install nodejs npm +root@server# update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10 ``` -2) Repare que a linha tem indicação para o terminal sh. Mude para bash e salve o arquivo com a linha desta maneira (use vim, nano ou qualquer editor de sua preferência): - - timtec:x:1001:1001::/home/timtec:/bin/bash -* Atualize o índice de pacotes e instale o git +* Nodejs no **DEBIAN** ``` -root@server# apt-get update -root@server# apt-get install git +root@server# apt-get install curl +root@server# curl -sL https://deb.nodesource.com/setup | bash - +root@server# apt-get install nodejs ``` +* Instalando o **POSTGRES** -* Com usuário da aplicação - no nosso caso timtec-production - faça a clonagem do repositório: +Recomendamos o postgreSQL, mas o django suporta outros bancos de dados relacionais. ``` -timtec-production@server$ git clone ~/https://github.com/hacklabr/timtec.git +root@server# apt-get install postgresql ``` -Em seguida, escolha a versão desejada e atualize o código para ela com o comando abaixo. Aqui você encontra uma lista de versões do TIMTec: https://github.com/hacklabr/timtec/releases +* Instale o servidor web **nginx** e o servidor de aplicação **UWSGI** + ``` -timtec-production@server$ cd timtec -timtec-production@server$ git checkout +root@server# apt-get install nginx uwsgi uwsgi-plugin-python ``` -Substitua a tag da versão por uma tag do git válida. Ex: `git checkout v4.0` +## Crie o usuário da aplicação no servidor -## Dependências -Primeiro, vamos instalar as dependências: +Sugerimos que o usuário usado para fazer a instalação não seja o root. Aqui, o usuário que está fazendo a instalação é o **timtec-production**. Assim, nos comandos abaixo, substitua o nome do usuário pelo que você estiver usando. Os scripts possuem uma variável para definir o usuário que executará o proxy (wsgi). Caso use um usuário diferente, olhe o arquivo Makefile e faça as alterações necessárias. +Este tutorial pressupões que você esteja logado com o usuário timtec-production, que ele esteja no grupo sudo. + +É possível verificar se o usuário timtec-production existe através do comando: ``` -root@server# apt-get update -root@server# apt-get install -y libpq-dev libjpeg-dev libpng12-dev build-essential python-dev gettext python-virtualenv +root@server# grep timtec-production /etc/passwd ``` -### Instalando o nodejs - -#### Ubuntu +Caso o usuário exista, o comando acima deve retornar uma linha semelhante a seguinte: ``` -root@server# apt-get install -y nodejs npm +timtec-production:x:999:999::/home/timtec-production: ``` +Caso não exista é possível criá-lo com: -Diferente de todas as outras distribuições, o ubuntu usa o comando node para o nodejs por padrão, então vamos fazer isso: ``` -root@server# update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10 +root@server# useradd --groups sudo --create-home timtec-production ``` +Depois mude a senha caso não se lembre: -#### Debian -<<<<<<< HEAD:docs/instalacao_e_configuracao/timtec_deploy.md ``` -root@server# apt-get install curl -root@server# curl -sL https://deb.nodesource.com/setup | bash - -root@server# apt-get install nodejs +root@server# passwd timtec-production ``` -======= - # TODO: atualizar este endereço - sudo apt-get install curl - sudo curl -sL https://deb.nodesource.com/setup | bash - - sudo apt-get install nodejs ->>>>>>> 4.0-dev:docs/instalacao_e_configuracao/Instalação.md -Mais informações [neste link](https://github.com/joyent/node/wiki/installing-node.js-via-package-manager#debian-and-ubuntu-based-linux-distributions) -### Banco de dados -Recomendamos o postgreSQL, mas o django suporta outros bancos de dados relacionais. +## Obtendo o código + +* Com usuário da aplicação - no nosso caso **timtec-production** - faça a clonagem do repositório: + ``` -root@server# apt-get install -y postgresql -root@server# sudo su - postgres -c "createuser -d timtec-production" +timtec-production@server$ git clone ~/https://github.com/hacklabr/timtec.git ``` -<<<<<<< HEAD:docs/instalacao_e_configuracao/timtec_deploy.md -Com usuário da aplicação, crie então a base: +* Faça checkout para versão atual (v4.1) + ``` -timtec-production@server$ createdb --encoding "UTF-8" --locale "pt_BR.UTF-8" timtec-production +timtec-production@server$ cd timtec +timtec-production@server$ git checkout v4.1 ``` -obs: caso ocorra algum problema relacionado a locale faltante no sistema, [veja como alterar o locale para pt_BR](Alterando-locale-para-pt_BR.md). -======= - $ sudo apt-get install -y postgresql - $ sudo su - postgres -c "createuser -d timtec" - $ createdb --encoding "UTF-8" --locale "pt_BR.UTF-8" timtec -obs: caso ocorra algum problema relacionado a locale faltante no sistema, [veja como alterar o locale para pt_BR](Alterando-locale-para-pt_BR.md). ->>>>>>> 4.0-dev:docs/instalacao_e_configuracao/Instalação.md +## Criando Banco de dados -### Ambiente virtual python e dependências de javascript - -Em seguida, vamos criar o ambiente virtual python e instalar as dependências do python e do nodejs. O comando abaixo automatiza todo este processo, mas deixamos abaixo a opção manual. +* Como root, Crie um usuário para o banco de dados: ``` -$ cd ~/timtec -$ make install +root@server# sudo su - postgres -c "createuser -d timtec-production" ``` -Se ocorrer algum erro, tente rodar o comando make novamente, pois falhas podem ocorrer devido a problemas com a internet. +* Como usuário da aplicação - logado como **timtec-production** - crie a base de dados -### Criando ambiente virtual manualmente -Em seguida, vamos criar o ambiente virtual python: +``` +timtec-production@server$ createdb --encoding "UTF-8" timtec-production +``` - $ virtualenv /home/NOME-DO-SEU-USUARIO-OU-DIRETORIO/env - & source /home/NOME-DO-SEU-USUARIO-OU-DIRETORIO/env/bin/activate +## Instalação -Se você estiver seguindo a documentação, você pode deverá dar o comando da seguinte maneira: +* Utilize o make para rodar todos os comandos necessários para a instalação da aplicação propriamente dita - $ virtualenv /home/timtec-production/env - $ source /home/timtec-production/env/bin/activate +``` +timtec-production@server$ cd ~/timtec +timtec-production@server$ make install +``` -Agora vamos instalar as dependências: +> **ATENÇÃO 6:** Se ocorrer algum erro, tente rodar o comando make novamente, pois falhas podem ocorrer devido a problemas com a internet. - cd timtec - make install +### Criando ambiente virtual manualmente -Se ocorrer algum erro, tente rodar o comando make novamente, pois falhas podem ocorrer devido a problemas com a internet. +Caso precise criar o ambiente virtual do python manualmente, você pode usar isto: +``` +timtec-production@server$ virtualenv /home/NOME-DO-SEU-USUARIO-OU-DIRETORIO/env & source /home/NOME-DO-SEU-USUARIO-OU-DIRETORIO/env/bin/activate +``` -### Servidor web e de aplicação +Se você estiver seguindo a documentação, você pode deverá dar o comando da seguinte maneira: -Instale o servidor web (nginx) e o servidor de aplicação (uwsgi): +``` +timtec-production@server$ virtualenv /home/timtec-production/env +timtec-production@server$ source /home/timtec-production/env/bin/activate +``` - $ sudo apt-get install -y nginx uwsgi uwsgi-plugin-python +## Servidor web e de aplicação -Na pasta timtec/scripts/conf temos exemplo de arquivos de configuração. Copie os mesmos para seus locais e edite-os confore sua necessidade: +* Na pasta timtec/scripts/conf temos exemplo de arquivos de configuração. Copie os mesmos para seus locais e edite-os confore sua necessidade: - $ sudo cp ~/timtec/scripts/conf/timtec-production.ini /etc/uwsgi/apps-available +``` +root@server# cp ~/timtec/scripts/conf/timtec-production.ini /etc/uwsgi/apps-available +``` -Crie um link simbólico de apps-available para apps-enable: +* Nas configurações do UWSGI, crie um link simbólico de apps-available para apps-enable - $ sudo ln -s /etc/uwsgi/apps-available/timtec-production.ini /etc/uwsgi/apps-enabled/timtec-production.ini +``` +root@server# ln -s /etc/uwsgi/apps-available/timtec-production.ini /etc/uwsgi/apps-enabled/timtec-production.ini +``` -Inicie o serviço do servidor de aplicação: +* Inicie servidor de aplicação - $ sudo service uwsgi start +``` +root@server# service uwsgi start +``` -Copie os scritps de configuração da instância timtec-production para os sites available do nginx: +* Copie os scritps de configuração da instância timtec-production para os sites available do nginx + +``` +root@server# cp ~/timtec/scripts/conf/nginx-timtec-production /etc/nginx/sites-available/timtec-production - $ sudo cp ~/timtec/scripts/conf/nginx-timtec-production /etc/nginx/sites-available/timtec-production +``` +* Edite o arquivo de configuração do nginx para colocar seu domínio +``` +root@server# vi /etc/nginx/sites-available/timtec-production -Crie link simbólico do projeto de sites-available para sites-enable: +EDITE O QUE PRECISAR NESTE ARQUIVO +``` - $ sudo ln -s /etc/nginx/sites-available/timtec-production /etc/nginx/sites-enabled/timtec-production +* Crie link simbólico do projeto de sites-available para sites-enable -Remova o arquivo de configuração padrão do nginx para não haver conflito: +``` +root@server# ln -s /etc/nginx/sites-available/timtec-production /etc/nginx/sites-enabled/timtec-production +``` - $ sudo rm /etc/nginx/sites-enabled/default +* Remova o arquivo de configuração padrão do nginx para não haver conflito -Faça um reload do nginx para se certificar que ele está rodando corretamente: +``` +root@server# rm /etc/nginx/sites-enabled/default +``` - # sudo nginx -s reload +* Faça um reload do nginx para se certificar que ele está rodando corretamente -Obs: se o nginx não estiver rodando, execute: +``` +root@server# nginx -s reload +``` +* Se o nginx não estiver rodando, execute: - $ sudo service nginx start +``` +root@server# service nginx start +``` -Em seguida, edite o arquivo de configuração do nginx para colocar seu domínio. A instalação não terminou ainda! Precisamos criar o usuário inicial, configurar o domínio do django, o envio de email e a API do youtube. diff --git a/themes/timtec/static/img/illustrations/home-l.png b/themes/timtec/static/img/illustrations/home-l.png index 6c8280e93..455860e34 100644 Binary files a/themes/timtec/static/img/illustrations/home-l.png and b/themes/timtec/static/img/illustrations/home-l.png differ diff --git a/themes/timtec/static/img/illustrations/home-m.png b/themes/timtec/static/img/illustrations/home-m.png index c1c722c78..31171732a 100644 Binary files a/themes/timtec/static/img/illustrations/home-m.png and b/themes/timtec/static/img/illustrations/home-m.png differ diff --git a/timtec/settings.py b/timtec/settings.py index ab4bb223d..be76ad0e2 100644 --- a/timtec/settings.py +++ b/timtec/settings.py @@ -460,6 +460,16 @@ ), 'output_filename': 'js/certificate.js', }, + 'edit-class': { + 'source_filenames': ( + 'js/directives/waiting-screen.js', + 'js/edit-class/app.js', + 'js/edit-class/controllers.js', + 'checklist-model/checklist-model.js', + 'js/factories/timtec-models.js', + ), + 'output_filename': 'js/messages.js', + }, 'codemirror': { 'source_filenames': ( 'codemirror/lib/codemirror.js',