From bece8852269fe3c9cc1146b83143402b1ee43726 Mon Sep 17 00:00:00 2001 From: perro Date: Fri, 20 Jan 2023 14:01:45 -0800 Subject: [PATCH] =?UTF-8?q?Habilitaci=C3=B3n=20de=20b=C3=BAsquedas=20perro?= =?UTF-8?q?nas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 1 + source/main/admin.py | 4 +- source/main/migrations/0001_initial.py | 4 +- .../main/migrations/0003_movie_is_digital.py | 4 +- .../migrations/0005_auto_20230109_1810.py | 18 +- source/main/models.py | 157 ++++++++++++++---- source/main/static/css/main.css | 46 +++++ source/main/static/img/favicon.png | Bin 1967 -> 7088 bytes source/main/static/img/favicon.svg | 130 +++++++++++++++ source/main/static/img/logo-mauflix.png | Bin 22434 -> 5839 bytes source/main/static/img/logo-mauflix.svg | 130 +++++++++++++++ source/main/views.py | 13 +- source/mauflix/settings.py | 1 + source/mauflix/urls.py | 6 +- source/templates/base.html | 2 +- source/templates/help.html | 103 ++++++++++++ source/templates/home.html | 8 +- source/templates/info-body.html | 18 +- source/templates/info-head.html | 6 +- source/templates/nav.html | 5 +- source/templates/search.html | 28 +++- source/templates/section.html | 4 +- 22 files changed, 626 insertions(+), 62 deletions(-) create mode 100644 source/main/static/img/favicon.svg create mode 100644 source/main/static/img/logo-mauflix.svg create mode 100644 source/templates/help.html diff --git a/requirements.txt b/requirements.txt index 9881b6f..49e6533 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ django-admin-list-filter-dropdown wikipedia-api bs4 lxml +unidecode diff --git a/source/main/admin.py b/source/main/admin.py index 85e25e3..e447f68 100644 --- a/source/main/admin.py +++ b/source/main/admin.py @@ -98,7 +98,9 @@ class AdminMovie(admin.ModelAdmin): return def _public_telegram(self, message, cartel): - url = f"https://api.telegram.org/bot{settings.TOKEN_TELEGRAM}/sendPhoto" + url = ( + f"https://api.telegram.org/bot{settings.TOKEN_TELEGRAM}/sendPhoto" + ) url_cartel = settings.URL_CDN.format(cartel) data = { "chat_id": settings.CHAT_ID, diff --git a/source/main/migrations/0001_initial.py b/source/main/migrations/0001_initial.py index 23749fa..9ddee64 100644 --- a/source/main/migrations/0001_initial.py +++ b/source/main/migrations/0001_initial.py @@ -79,7 +79,9 @@ class Migration(migrations.Migration): ), ( "is_actor", - models.BooleanField(default=False, verbose_name="Es Actor"), + models.BooleanField( + default=False, verbose_name="Es Actor" + ), ), ( "is_director", diff --git a/source/main/migrations/0003_movie_is_digital.py b/source/main/migrations/0003_movie_is_digital.py index 800ffdf..608ed2b 100644 --- a/source/main/migrations/0003_movie_is_digital.py +++ b/source/main/migrations/0003_movie_is_digital.py @@ -13,6 +13,8 @@ class Migration(migrations.Migration): migrations.AddField( model_name="movie", name="is_digital", - field=models.BooleanField(default=False, verbose_name="Es digital"), + field=models.BooleanField( + default=False, verbose_name="Es digital" + ), ), ] diff --git a/source/main/migrations/0005_auto_20230109_1810.py b/source/main/migrations/0005_auto_20230109_1810.py index 1d899e9..c8b8fee 100644 --- a/source/main/migrations/0005_auto_20230109_1810.py +++ b/source/main/migrations/0005_auto_20230109_1810.py @@ -6,28 +6,28 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('main', '0004_auto_20210807_2207'), + ("main", "0004_auto_20210807_2207"), ] operations = [ migrations.AlterField( - model_name='country', - name='id', + model_name="country", + name="id", field=models.AutoField(primary_key=True, serialize=False), ), migrations.AlterField( - model_name='gender', - name='id', + model_name="gender", + name="id", field=models.AutoField(primary_key=True, serialize=False), ), migrations.AlterField( - model_name='movie', - name='id', + model_name="movie", + name="id", field=models.AutoField(primary_key=True, serialize=False), ), migrations.AlterField( - model_name='person', - name='id', + model_name="person", + name="id", field=models.AutoField(primary_key=True, serialize=False), ), ] diff --git a/source/main/models.py b/source/main/models.py index 3449778..c8d3335 100644 --- a/source/main/models.py +++ b/source/main/models.py @@ -1,10 +1,12 @@ import random import time +import re import wikipediaapi from bs4 import BeautifulSoup from django.conf import settings from django.db import models from pathlib import Path +from unidecode import unidecode class Gender(models.Model): @@ -71,14 +73,15 @@ class Person(models.Model): class MovieQuerySet(models.QuerySet): - def random_pick(self, random_max=6, min_items=20, **kwargs): + def random_pick(self, random_max=6, min_items=20, all=None, **kwargs): """ Regresa películas de manera aleatoria. Por defecto tiene que haber al menos 20 películas en la consulta. Por defecto regresa un máximo de 6 películas. """ - all = list(Movie.objects.filter(**kwargs).values()) + if all is None: + all = list(Movie.objects.filter(**kwargs).values()) if len(all) < min_items: return None elif len(all) < random_max: @@ -94,7 +97,7 @@ class MovieQuerySet(models.QuerySet): Por defecto regresa las primeras 6 películas como máximo. """ all = list(Movie.objects.order_by(f"-{key}").values()) - return all[:top_max] + return self.fix_all(all[:top_max]) def top_random_pick(self, key, top_max=6): """ @@ -113,9 +116,10 @@ class MovieQuerySet(models.QuerySet): else: break if len(top) > top_max: - return random.sample(top, top_max) + movies = random.sample(top, top_max) else: - return top + movies = top + return self.fix_all(movies) def home_pick(self): """ @@ -139,9 +143,9 @@ class MovieQuerySet(models.QuerySet): Las secciones son novedades, mejor valorados y más descargados. """ sections = { - "Novedades": self.fix_all(self.top_pick("id")), - "Mejor valorados": self.fix_all(self.top_random_pick("stars")), - "Más descargados": self.fix_all(self.top_pick("count")), + "Novedades": self.top_pick("id"), + "Mejor valorados": self.top_random_pick("stars"), + "Más descargados": self.top_pick("count"), } sections["genders"] = {} return sections @@ -161,6 +165,8 @@ class MovieQuerySet(models.QuerySet): Los enmiendos son formateos de unos valores (que se guardan en nuevas llaves) y de rutas a medios. """ + if len(movie["file_name"]) == 0 or len(movie["cartel"]) == 0: + print(f"WARN: película sin ruta de video o cartel:\n{movie}") movie["duration_formatted"] = self.format_duration(movie["duration"]) movie["count_formatted"] = self.format_count(movie["count"]) movie["stars_icons"] = self.format_stars(movie["stars"]) @@ -177,7 +183,7 @@ class MovieQuerySet(models.QuerySet): ('DEBUG'). La URL_DEBUB apunta a la dirección en nebula. """ if settings.DEBUG: - if str(Path(el).parent) == '.': + if str(Path(el).parent) == "." and len(el) > 0: el = f"{el[0]}/{el}" return settings.URL_DEBUG.format(el) else: @@ -282,53 +288,146 @@ class MovieQuerySet(models.QuerySet): except Exception: return None + def get_related(self): + """ + Regresa los campos relacionados a las películas. + """ + return ["countries", "genders", "directors", "actors"] + def get_movie_by_id(self, id): """ Obtiene película por id. Esta obtención también añade objetos relacionados. """ - related = ["countries", "genders", "directors", "actors"] + related = self.get_related() movie = Movie.objects.prefetch_related(*related).get(pk=id).__dict__ - for key, val in movie['_prefetched_objects_cache'].items(): - movie[key] = list(map(lambda x: x['name'], val.values())) + for key, val in movie["_prefetched_objects_cache"].items(): + movie[key] = list(map(lambda x: x["name"], val.values())) movie.pop("_state") movie.pop("_prefetched_objects_cache") self.fix_data(movie) return movie - def get_movies(self, prefix, query): + def get_movies(self, query): """ Obtiene películas buscadas. - - El 'prefix' permite filtrar la consulta por el campo seleccionado. """ - selector = self.get_movie_selector(prefix) - return f"selector: {selector}; query: {query}" + movies = Movie.objects.prefetch_related(*self.get_related()) + for q in self.get_queries(query): + selector = None + if re.match(r"^\w:", q) is not None: + selector = self.get_selector(re.sub(r"^(\w):.*", r"\1", q)) + q = re.sub(r"^\w:(.*)", r"\1", q) + movies = self.get_movies_by_query(selector, q, movies) + if len(movies) != len(Movie.objects.all()): + if hasattr(movies, "values"): + movies = self.fix_all(list(movies.values())) + movies = self.random_pick(random_max=100, min_items=0, all=movies) + return movies + else: + return [] - def get_movie_selector(self, prefix): + def get_movies_by_query(self, selector, query, movies): + """ + Obtiene película por query. + """ + if selector is None: + return self.get_movies_by_query_any(query, movies) + elif selector != "section": + return self.get_movies_by_query_selector(selector, query, movies) + else: + return self.get_movies_by_query_section(query, movies) + + def get_movies_by_query_selector(self, selector, query, movies): + """ + Obtiene película por query que tiene selector 'w:'. + """ + if selector == "year": + selector = f"{selector}__iregex" + try: + query = int(query) + except Exception: + query = 0 + else: + if selector == "name" or selector == "original_name": + selector = f"{selector}__unaccent__iregex" + else: + selector = f"{selector}__name__unaccent__iregex" + query = f"[[:<:]]{query}" + kwargs = {selector: query} + return movies.filter(**kwargs) + + def get_movies_by_query_section(self, query, movies): + """ + Obtiene película por query que tiene selector 'w:' para sección. + """ + if query == "mejor valorados": + return self.top_random_pick("stars", top_max=100) + elif query == "novedades": + return self.top_pick("id", top_max=100) + elif query == "mas descargados": + return self.top_pick("count", top_max=100) + else: + return list(movies.values()) + + def get_movies_by_query_any(self, query, movies): + """ + Obtiene película por query que no tiene ningún selector. + """ + for field in ["name"] + self.get_related(): + if field != "name": + field = f"{field}__name" + kwargs = {f"{field}__unaccent__iregex": f"[[:<:]]{query}"} + result = movies.filter(**kwargs) + if len(result) > 0: + movies = result + return movies + + def get_queries(self, query): + """ + Devuelve un conjunto de queries sanitizado; p. ej.: + de: d:Bruno--Stagñaro y:1997 pizzá, birra, faso + a: ['d:bruno-stagnaro', 'y:1997', 'pizza', 'birra', 'faso'] + """ + queries = re.sub(r"\s+", " ", unidecode(str(query)).lower()).split() + return list(map(lambda q: self.clean_query(q), queries)) + + def clean_query(self, word): + """ + Limpia query de caracteres 'W' al inicio y al final. También sustituye + '-' por un espacio. + """ + word = re.sub(r"^\W+", "", word) + word = re.sub(r"\W+$", "", word) + word = re.sub(r"-+", " ", word) + return word + + def get_selector(self, prefix): """ Obtiene el selector para filtrar la consulta de una búsqueda. Esto permite restricciones en la búsqueda con esta sintaxis: - * t:Un Título => Buscará las películas que tengan 'Un T…' en sus 'name' + * t:Un-Título => Buscará las películas que tengan 'Un T…' en sus 'name' * d:Nombre => Buscará las películas que tengan 'N…' en sus 'directors' + El guion (-) se usa como separador de palabras, puede contener acentos + o eñe, aunque va a ser decodificado. """ prefixes = { - 't': 'name', - 'o': 'original_name', - 'y': 'year', - 'c': 'countries', - 'p': 'countries', - 'd': 'directors', - 'a': 'actors', - 'g': 'genders', - 's': 'section', + "t": "name", + "o": "original_name", + "y": "year", + "c": "countries", + "p": "countries", + "d": "directors", + "a": "actors", + "g": "genders", + "s": "section", } if prefix in prefixes: return prefixes[prefix] else: - return '' + return None def upload_cartel(instance, filename): diff --git a/source/main/static/css/main.css b/source/main/static/css/main.css index 1533953..82c1966 100644 --- a/source/main/static/css/main.css +++ b/source/main/static/css/main.css @@ -19,6 +19,10 @@ display: flex !important; } +.navbar-item img { + width: 100%; +} + /* Cada flecha en los títulos de secciones */ .arrows { font-size: 1.5rem; @@ -291,6 +295,21 @@ section#notice a { margin: auto; } +/* SEARCH */ + +.search-div > * { + display: inline-block; +} + +.search-div input { + width: calc(100% - 4.1rem); +} + +.search-div button { + width: 3.8rem; + height: 3.8rem; +} + /* ICONS; cfr: https://css.gg */ .gg-time { @@ -396,3 +415,30 @@ section#notice a { border-bottom-right-radius: 40px; right: -6px } + +.gg-search { + box-sizing: border-box; + position: relative; + display: block; + transform: scale(var(--ggs,1)); + width: 16px; + height: 16px; + border: 2px solid; + border-radius: 100%; + margin-left: -4px; + margin-top: -4px +} + +.gg-search::after { + content: ""; + display: block; + box-sizing: border-box; + position: absolute; + border-radius: 3px; + width: 2px; + height: 8px; + background: currentColor; + transform: rotate(-45deg); + top: 10px; + left: 12px +} diff --git a/source/main/static/img/favicon.png b/source/main/static/img/favicon.png index 2ff469259dd2c193ced1e31653ea42e6c5266809..d42379d9adf064b4b23998b7e8647ed82f9ce83d 100644 GIT binary patch literal 7088 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 IUk7~uU&1T(C;$Ke literal 1967 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&t&wwUqN(1_s8~OlRi+PiJR^f};Gi%$!sP z291fe6Ky>XJIEZ34_;aqBJxJz(S(Iv&9NazHij+a5OrF6VO8_VrSTV*D0&taio|g9 z)#-%E#>^=#Ef8fn$S>fouI?(X`LW(}q3as<<9{p4-xq%86Ub6s7AYI(a5>jlvcL1? znN<=MuKK$a6Br&FZSJx3^jh%t$FBDN_2q)|uKv3-f3g4BD4q|UM*`Dkn0;jXcxG8{ z!98`pjZJHW<_r7JC~tVnCW#+xV__O4Y#)L8Lnk+Hoh`prgJ~F zZaJ>?T=!hm%0p7$pXl?XeVv-R?y`)dOm{>Riy)Vl#v&GJ!Kl(89hFS`Ysc*0{8BB_ ze4^DfF_|$nV?wO`5BWduuATDrt@1o5apC793;D$k3F0flWuO1PQTR%|;lJdLOSAtK z@y__GviH`NL+%d$%?b+dUYTP1pom?f$|xncOx503aXRz8^Dkx_9eAL7Ve_-E*KQnt z$vk1vIwA7|2NeybEr0XX?ESl}w{Pzo_x`J6h|VmU`|$Rad%$?%EbxddW?;}$24TjE zrS@e&LG}_)Usv{L+?-5;Z2R@=-UEdsOI#yLobz*YQ}ap~oQqNuOHxx5$}>wc6x=<1 z1Hv2m#2FY^XL!0ehE&{od)1JUfq~<&!GZnHA2v>RahhK?iRr=oi3|rA7z`K~co-O% z$zl$ycA%pvaXjVPf!-%O=pZ3Zp?0A6$%{C+HVU=#kd=d#Wzp$PyJx@$N9 diff --git a/source/main/static/img/favicon.svg b/source/main/static/img/favicon.svg new file mode 100644 index 0000000..86d0fdd --- /dev/null +++ b/source/main/static/img/favicon.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/source/main/static/img/logo-mauflix.png b/source/main/static/img/logo-mauflix.png index e0c704fe35929578f1a524b415f52832797110ea..fd1eac664687380e6ab8b7f0bb37e49f4472fbf1 100644 GIT binary patch literal 5839 zcmZ8lc{r5s*MBToN)&};Nm7>VL}ZC%S9XT1S+a()8>WatQ<7cCI+h`X%989$h_R(C z*=Ouq*2eFC`u^VUd%f=;bIo<#&-2{(=X}mNpL5Rr?5?ib$>VItAqYCDp?*srg2)2E z52B$0E5^c9lHiZ_p}Ltj1kpFaKe9Ft?0v9!4ykI2H1Kdh`q_BdLwW*k}h*AR;O|HRgP%2`Prr*7eaArd~(8l zSso~pY(M_&gV{z+ANN!q4X;ZmM}p-~S%V^GW9J$4iWhsE(k(~{#oMat(&3gZ8QMzI za$A+O$-=H?GjY_dbFtB9@dFvLudH%TvFf%Jhb*zT7(xdw7>w%r>97`uk6@!o`!C!Z z6VJ{Ql3VbS#U3mfPbeTsZ6yBkr=Y1*LbbCITpVkdt)}?HC$?D^eRi6;1%!I=ug)$a zbBN(0<>V0Yt+`%_gYLP)IgRziLsU;om5Xqm#{)T^t6%%GW*4_cTXB}Ov=BDq>6)m{ zr;_Sc`W1c?j*?W_o;$@eA_G?yZfm)p&)q&><1GgZ@`c zTL(cN{V68Wgz6&8p$>!m^BdP7v{rkEdf!r~S7oV2JUj*Yu$C=%p;ZRr~^HSX#^(p_^YIt?! zqlVud_I@mxRhOW>lzX+-bi`Ia)8^$OALA71aYjg4*G=f=aF(%Uz-O)jHc8_UV{f4h z^S7QVfqK4Fkk9K=EI8F_OmPA4Q+HzJ&UUA7_l|0KGR+Or zs5chE8m^YO)g8@Y5=}N(1cciM3RMmGhMT3&%>wdUv7oUFT7SSN^OWk3%+9Y0< zuifw{R6w;NFF{W4a>7~1%sp{|z5=2gEuUE|MRKc_igQG@ygAKqI5`+}Fi0Fi32oR} zmg%}0jM<>Th`5|&E4t?%VJwU-=9kN6CR0K`i=i2IlfIW7 z`e%Ct&Az6OE}0QA4DuV9eRD!`r*HoKL6=G%4;gxNzItG(V760ROPpi&%CRL)k}}8f z@~weGemDpTe2}tf(hpqJV&G!gcc#a-4bsH*P?GVFk)awFbNRJhHVzpJds#;GsaM0| zMj$zgE?gNyzLwq8$fzy#5$PDtNYox4l$T4wkYAwuz^=wajU0|+(55dW6FJtvQF`+1 z`LhMr@ezwr!cShmAzMq{dQI6~c-18T+V{L{Nz=RZdx$@sQ3GY7<|h(ygXdRWr8%h4 zs#KPiS#oU*;^tc4)NdpB>V){;<}JB)7o~3f$hR8mAmV{tG`8JSc1VwwQy^%ZWHqOc z;hzj^72s6)gn9;R)iUsM=6)#{$_q5xf`a{}SLpPEwOgRFPLwIX+HDZJl3@xfDo|u!u@1Li9 zAe!@N-lNjq(FFM*`&ieIzAu%jfY9}u6#|h3-apv!dNH&g!3lv#SDeuafx#McbG#np zZ}z$TBF@6}Tt9`vBp2Hh1FdHtVyh??@!2Mtxzsd2yG<7`nt7UWI?LydYWE%DTjBfF zKz4yfY96n652s}uFoSQK-z~r9GM%w<@jZYtX?cETQSdsqF#}K^$~OynI$K?0CZ3s7 z7g;wZ41##;%@06o_*KJC%iUE!rd+qjrd(-B#V|fpo^!xPw5#h3a5<7JfC%O<(a|WK z{$-P*Q203{s8@#S@Ws3Oq0BOil9Zb1K%~I8G&N%vIlzRHQ)24d?*?7*V;3b?5N>?N z8$Pc=^a{=yW2ZiidwtyHq0ru|O0fP-Nfpdd_Np}dp8{9}a;_VNtn^)$camj1rSB!K zFIiJ=;pT0ggkh9SyITz0{d-TS((5L#y(2m!*j!80gOQ@XUgpY#DByZ53h{t6?E=&> z_>vI6r9?iR0Z`$*Y)alm*c-Vm0fB*ihCOrS3U|%58qgl>#v9KK4_w>Jr)~&^OdaF6 z9{3Y^=>nFwS#@#cS=eBW;2Ztts<=liFrOHpdro_s`>Ua@QXJGYnu+o6hfr}PMqakX z1U}=*wAw_t7_Z~9--k3Y3nA)tjoJVX;$sB)#KUII@hd=;3iv+fa+>a^k;?2N2{KAb zfgyRp}B zpQ@H??LMDk;h(9MzA_(>6Fn%9$06~tDOj`kSpoj9;vBp=?_TXx)P9{qDCB45F+W17 zhoeW`x{OZbi%);Z|@je6s5m8uB}F^bLMrj z5$IF|S^nu%_~gRuVMXqZ&X<@xIh7U+Jq4Pm5!) z1_JherplwUCqcfnkZBN*F>!j%*X8hCmz2%OS$1lAYq~vXxU^VZHQ-!IHS+nj5cTip z$W<)64B?F)ry=or5BUM0$oDgpe@Nr0NYE!m#n1o=<^DP=!*)HhCOz0%oI-)--94sT z&Z}%orb)Tif$P*VR>U}Hxi8D}0zv2+EYPVU@L{PVf?sxkR;BzvaH~0Gcx%SHbaCmU zcOK8NVr=_muNs#SQ#Z!bd6$139eOGHt<+*nLJf|V)*p6HGdi=dO0-(!=Ea`Qo6h94 z5H{Yp*|F?fVC(O(y=0mR2Mb2zcPeH^BKGtz=XCbcnQ+tGYtaT3yH32#xA`v%=$G}y zMm={g9Kp7G94THFDA&`;n3v}?;!rHGYb!GH$wC-SkaltkF(!*6&zzSPEpo%uFoYr% zwaTM!dPSdaUb@mstk)POmcJUx%g%hnx^c7E&HaSDRA>}ufNvdXqu*S1bv+4V=<(~9 zO{eR)wi!VKR6~8^z*qXIE&RM?4%oI^s{2wex;)QtG3D+iVP!w(?Qfk!1+2Mn-0;%2 z;ygsue7=!$FNnc;csy)Bc!K*4R$;cOxgP&HDCp1r+|8)y2hOs`+1g`gTn`^c=EDkW z_Dy{?cq@5M=w7z`0YfUrOAi>|+3Q^LcuBIdQ(ey?uv_VlR#Y`37d{z%uW3>gb7t25+WaH_3GYeu_^OqWBAncBr?!G<9RPZIuVqwh0pOW`PG` zceY#e+@8yt&HC0zI#@I4oajF)F?ELjdY0Ng zn5Lr4s|`WP++MZJaaec#LYazp>>(~QC=7 zEVkIWtfDZmG!}YmGeIF=hYOVlCX{&Q`2v%C^6iz;+o0y=O@Gnq$xtk1Q2379KCdt! z7#eC7`GV5~;q}YX)=g3F5`9pN>_tkt@-7FT_zpHDWVl4f+`Nh#Qb; z^fDXtE^+$ZD{83dpJmn!$Xck%1!}M@2CsS11wil4{*Z#KMVwE9wV&(9p#-~j3UuyB z2^o~?`RgpS)N&oX!>+;!T~)=AL8dV$N+D~po(>B1EWDQ>yqBeg|1l`qQy09`_L3UX zD!WGkU7Kzcghryh!8_MsC{YLs=p8G3l$h~<*G8h?r+-iW3*<~C6?m#U453?Ot}y%q z1P22Nf`MFZ{TE1!?@4InFA$M`f$;w|;Qs&d$pMUYZ(lr4hF(JjrsRu0w9!i9#H21^XCrpcP1aEGYwGR^_OE;*nq$Ml>y%W zamN4~Si?#|P&B;u_dVmZzm~zIwQ>MX|6%E0Al)#KF_>che;Hx6C(GVBX6_~Ubl1%eX1 z_q9zYJ!_J;O_0I^9s-fc7_x>Fr2WnxflCusQXEY(FWR!-F%9Nx&(qL=9J|IrhE7aV z@HRGTyOV-Beaf^EPzxI)2GbqG4M>H0dF+6m zy>I@2J{e>4vy5rb>srW%Cj3V?4B;vSiOA{uJca{2ep=loPo+UPV}+#ITVMMH8Iu{u z%7W7v-iht<1hrq)0@L&|(r>m(doa0Teyh=CH3f8kjX&N0tfYI=`TKJjHLX2?``c7w zA?-FAHoq7ytgZ!17=z5KMxeOa-t#@FJ+|!mF11%K-q1ebe(vqM1Q))~PLj;B^J!5R zeA|H+D{dmBumLS^=RYs;!%kNH!h-pz?g{sgUyyV~hpP#9e7drXWJA+cn_G~Aor6t& zV|?lJlc?U>UB5p+@TEzBqg~?TUhvFl_Gx{d9c2zYR~^i5)A{eRTiL7?fMLJTYUd(e zj9e3_gFLd06_na!%;QrHFC*_<$7CnRMF>>e7ZUi$zRb#`NT5D!>~5knL}z|^>+tZr z-yH(=I7*VE_+VK6aguB425tOA{Iwc8)G!cd+@FNughLRi53hQN=E+y#EhA)!X5z088`B zNGpKFEWYV=T&%5ZlvVi0u@ptS@W1W<<5RP>H9qP0Mov zoxR_Z9?65icn}&iEkAbD9-4&dvj)@4u*{0nNg9vZihCk;_1}?n(>*~qj{jIA9VPdi zKGhS19h7!cDy83dYUBJf>*0}%(f0t8adF*K_82gaWP+K z-zw|mQZZzQmm+SEk8(^?7uwlW5m+Z+UJ2Rz;K=~33MUz^s!$r-3L(#Ez%ABXFDo}u z>D1M<$n?OzrVfa|)V`2vi)bxN719iMqr%_xZy?5CBO34k>bl_X=yDkG=QkeldZWb8Q7)U8{S}@JdDK=f>l%%w;Yl9s&w$Mf;0Es3aBpp%$+K4atYMlj&zLnS<3aTm zIvi97L)XFV=5g$3HRA1gyH{`y%7nV6t ziH*;DzBOjHoznR@){yza0uaobE=!r5#EH7PJL3mEsni8eJIPHid`;cPI`*U+Y!~1y zgA_eB{Bz)5->#v{ozwT@_|dL6+-H{7%^S%_4FavI-zB~q9%rhbUiU{{Z6~>96f58= g=6eoiD@a`Tq(%i;=BvJe%VgHl?=V%20%nAMp60drssrpQ{`|^p=u_J{oZH*hEB88_}iSk~4 zBYxw_-F%Ln&i{ggGd9?vt1GUrEF`MF3a=`d?i#YT{ikr7p5cLur6K$T0i!)SlsH_5 zyet^uFK0uu{WztJ9gSo$D&c9~!dRDmxV3dEXkJhj*h zZKV!ZY2V)wG!wZc!++F5vG{+)(%wA@@OIcJVO%OSquLIrJ&3~?ugB)!YrOY&To@&U zKjIkeI0Y6#MgkH|TpU)Po0(CglGhGxek+s5v` z4hQS!kXPFJgOmsaJ{gOqiV)PW%oCuxVk}U8CG@Kp_1qstqU9|m0yNQxJw*ooM^P^T{MS`a|36;#zkla{^ML=R=>IQO`+w^3f1=m^XG8y&`}=?1 zDfi#h$aQ62YbNLsy&xuz z2oC;I)@9MuwO7+)(bVJ6)U)TB+rO?5vk+4=@R_v<+c_+RG$|Yc(9??={Lvv!3vMY# zHq?mM(|JLRIv5=6Bhxk1FUpU4*QOySXZ1yo2#4XvGVy4iVpCwk2$1X8BSwCVL~gESigp8vIi z^!dEAqRH^}9*b*x4}u8ZyZdA{vDnMor~10*q(tJT%yca|PgNf4P;H-}s)*#YmAzw5 zQxG?meyxdc7uDEs%i|z|rq1D5z8?X=PMiEAfE!k|!sic zj~g{SNYZjTtb0r)@B8|lu4QFPXOGNMua`CWai2I-_Lg|X9-QSw?ZQ+0OI+`-i2`Pp zLf044=f3{e0E8m}iK1p+Xh3+lioS-xo%1c-&qW@+lNI@CU)dSYJF^}=KRZAB{v0Jh z*n9#1Vb>M0G>YqJU|H!Kk_rWH!UhZ~o-4}9`8M(;_mi4@xQCM64g0x>^Y9z}Mg)4> zxRAqjILG?s|Fb5Lfj-YaJ{%F@BfN>P*9)pE2Gq3-@!Y(USedWwA8S^}u69JTmqmuq z6jHhw0OFU^*0_Q{^Pg%eAp=H8uH$VbUhNfs8fl~M5C1|Q?za*AS;Ont)lU6qWnR+$ zT;xYFr2YN1ZwOmCANeFdKk@!vp9jVZes{4%mWIwYwQJ$11FyE0Qw?xXS&aedU)P}! zVDGlCSTa+Hm-FJQwj#TH@_<#u5$W)z=2dsOR$Q~wj^9WbR^5rloTV=DOh+T;6Kbg4 zF7Gnsn7SG^?RvHfNfRj%iV>Apg8U(_NpQ$I1PyrRl^gNxHw3T-sQ4A!M0N%i3F6*v z-d0AbDoI)AYnb)q)&1=Xt%MbfyuCAQe!T09`0~YF>BNKy4fsYyy+TJiL7-WR3?$Ns z;T_*tdESfMP%lpL?4bbJS=`To+?rL5xr&_3eqF*z^!+Qs1F7PdR|5FHAgdBPT5+H<=#X|EogwtWbcl&M5TbfZ4^Qt+uw^(eKn=*H>?*sxqY5uw!YR3dqEb@_aotW zSycG6JPBHz$g4j;b{%RlN|JufnBetUI^G8->Qfyk_UYjjOdCO-n>MhAUj-FSL3br zB-XzOCj*3S!8nux)I6%lS<$V}_22*wQP!iXR*%|gyFhd?AB-!5@4}_f03G_;cG%

#NiMnv2Zi2&BXje-+3~J9;u{VG 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 Hw|@Q~hVGUw diff --git a/source/main/static/img/logo-mauflix.svg b/source/main/static/img/logo-mauflix.svg new file mode 100644 index 0000000..02de9b1 --- /dev/null +++ b/source/main/static/img/logo-mauflix.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/source/main/views.py b/source/main/views.py index 9a5d294..6e51182 100644 --- a/source/main/views.py +++ b/source/main/views.py @@ -11,8 +11,12 @@ def home(request): return render(request, "home.html", context) -def search(request, prefix, query): - context = {"movies": Movie.objects.get_movies(prefix, query)} +def search(request): + if request.GET.get("q"): + query = request.GET["q"] + else: + query = "" + context = {"movies": Movie.objects.get_movies(query), "query": query} return render(request, "search.html", context) @@ -21,6 +25,11 @@ def about(request): return render(request, "about.html", context) +def help(request): + context = {} + return render(request, "help.html", context) + + def bugs(request): context = {} return render(request, "bugs.html", context) diff --git a/source/mauflix/settings.py b/source/mauflix/settings.py index 4156916..dea7bb1 100644 --- a/source/mauflix/settings.py +++ b/source/mauflix/settings.py @@ -48,6 +48,7 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "django.contrib.postgres", "main.apps.MainConfig", ] diff --git a/source/mauflix/urls.py b/source/mauflix/urls.py index df9feba..5a4e6ec 100644 --- a/source/mauflix/urls.py +++ b/source/mauflix/urls.py @@ -8,6 +8,7 @@ from django.conf.urls import include from main import views from main.feeds import LatestMoviesFeed + # ~ from main.api import ResourceMovies @@ -17,10 +18,9 @@ from main.feeds import LatestMoviesFeed urlpatterns = [ path("", views.home, name="home"), - path("search/:", views.search, name="search"), - path("search/", views.search, {'prefix': ''}, name="search"), - path("search/", views.search, {'prefix': '', 'query': ''}, name="search"), + path("search/", views.search, name="search"), path("about/", views.about, name="about"), + path("help/", views.help, name="help"), path("bugs/", views.bugs, name="bugs"), path("movie/", views.movie, name="movie"), path("ultimas/rss/", LatestMoviesFeed()), diff --git a/source/templates/base.html b/source/templates/base.html index 9e77e1d..67d53bc 100644 --- a/source/templates/base.html +++ b/source/templates/base.html @@ -14,7 +14,7 @@

-

Otras plataformas tienen todas las películas, excepto las que nos gustan :)

+

Mauflix. Otras plataformas tienen todas las películas, excepto las que nos gustan :)

{% include 'nav.html' %} diff --git a/source/templates/help.html b/source/templates/help.html new file mode 100644 index 0000000..5bc1b08 --- /dev/null +++ b/source/templates/help.html @@ -0,0 +1,103 @@ +{% extends "base.html" %} + +{% block content %} +
+
+

Ayuda

+
+

Sobre las búsquedas

+

En las búsquedas existen las siguientes consideraciones:

+
    +
  • + Las búsquedas tienen un límite de cien resultados. +
  • +
  • + Cualquier búsqueda ignora tildes, mayúsculas y eñes, + así que «Iñarritú» + es lo mismo que «inarritu». +
  • +
  • + Cuando una búsqueda encuentra más de cien películas, + los cien resultados son seleccionados y ordenados de manera aleatoria: + nunca obtendrás los mismos resultados. +
  • +
+

Refinamiento en las búsquedas

+

Para buscar por campos específicos se usan algunos de los siguientes prefijos:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
prefijo:ejemploDescripción
t:lucioBusca «lucio» en los títulos
o:monkeyBusca «monkey» en los títulos en idioma original
y:1989Busca películas publicadas en el año (year) «1989»
p:MéxicoBusca películas hechas en el país «México»
d:vardaBusca películas dirigidas por «varda»
a:umaBusca películas en donde actúe «uma»
g:dramaBusca películas del género «drama»
+

Esto añade las siguientes consideraciones:

+
    +
  • + Para dos o más palabras con prefijos se usan guiones en lugar de espacios, + como en «p:estados-unidos». +
  • +
  • + Se pueden usar varias palabras con prefijos o no para restringir la búsqueda, + como en «p:mexico d:buñuel cielo». +
  • +
+

API

+

+ Los resultados de las búsquedas pueden ser en JSON si usas la + API. +

+

Obtención de películas

+

+ La API para obtener películas usa la misma sintaxis a una búsqueda ordinaria, + solo cambia la url search por api. +

+

+ Es decir, en lugar de + search/?q=p:mexico+d:cuaron usa + api/?q=p:mexico+d:cuaron. +

+

Obtención de ficha

+

+ Para obtener una película en específico solo necesitas su ID. +

+ Por ejemplo, para obtener la ficha de Gremlins usa + api/?id=1596. +

+

Happy hacking 😎

+
+
+
+{% endblock %} diff --git a/source/templates/home.html b/source/templates/home.html index 9ed4f80..38a969e 100644 --- a/source/templates/home.html +++ b/source/templates/home.html @@ -2,7 +2,13 @@ {% block content %} {% for section, content in sections.items %} {% if section != "genders"%} - {% include 'section.html' with section=section content=content %} + {% if section == 'Más descargados' %} + {% if user.is_authenticated %} + {% include 'section.html' with section=section content=content %} + {% endif %} + {% else %} + {% include 'section.html' with section=section content=content %} + {% endif %} {% else %} {% for gender, val in content.items %} {% include 'section.html' with gender=True section=gender content=val %} diff --git a/source/templates/info-body.html b/source/templates/info-body.html index 62450f5..155647e 100644 --- a/source/templates/info-body.html +++ b/source/templates/info-body.html @@ -2,8 +2,10 @@ {% if request.get_full_path == "/" %}

{{ movie.stars_icons }}

-

{{ movie.duration_formatted }}

-

{{ movie.count_formatted }}

+

{{ movie.duration_formatted | safe }}

+ {% if user.is_authenticated %} +

{{ movie.count_formatted }}

+ {% endif %}
{% else %}
@@ -20,30 +22,30 @@ Título original{{ movie.original_name }} Año - {{ movie.year }} + {{ movie.year }} País {% for country in movie.countries %} - + {{ country }}{% if not forloop.last %},{% endif %} {% endfor %} Duración{{ movie.duration }} min Dirección {% for director in movie.directors %} - + {{ director }}{% if not forloop.last %},{% endif %} {% endfor %} Reparto {% for actor in movie.actors %} - + {{ actor }}{% if not forloop.last %},{% endif %} {% endfor %} Género {% for gender in movie.genders %} - + {{ gender }}{% if not forloop.last %},{% endif %} {% endfor %} @@ -59,7 +61,7 @@

No se encontró su artículo en Wikipedia, haz clic aquí para escribirlo o solicitarlo.

{% endif %}
- {% if user.is_superuser %} + {% if user.is_authenticated %} diff --git a/source/templates/info-head.html b/source/templates/info-head.html index 33462fd..9eb0c3a 100644 --- a/source/templates/info-head.html +++ b/source/templates/info-head.html @@ -3,8 +3,10 @@ {% if request.get_full_path != "/" %}

{{ movie.stars_icons }} - {{ movie.duration_formatted }} - {{ movie.count_formatted }} + {{ movie.duration_formatted | safe }} + {% if user.is_authenticated %} + {{ movie.count_formatted }} + {% endif %}

{% endif %} diff --git a/source/templates/nav.html b/source/templates/nav.html index 6a007b2..a1242bd 100644 --- a/source/templates/nav.html +++ b/source/templates/nav.html @@ -15,15 +15,18 @@ {% if request.get_full_path != "/" %} Inicio {% endif %} - {% if request.get_full_path != "/search/" %} + {% if request.path != "/search/" %} Buscar {% endif %} diff --git a/source/templates/search.html b/source/templates/search.html index eb1ac2b..9460127 100644 --- a/source/templates/search.html +++ b/source/templates/search.html @@ -2,6 +2,32 @@ {% block content %} -TODO: Búsqueda de {{ movies }} +
+
+
+ +
+ +
+
+
+
+ +{% if movies %} + {% include 'section.html' with section='Resultados de búsqueda:' content=movies %} +{% else %} +
+
+

+ {% if request.get_full_path == "/search/" %} + Explora el catálogo de Mauflix. + {% else %} + ¡Ups! Tu búsqueda no arrojó ningún resultado. + {% endif %} +

+

Visita la ayuda para obtener mejores resultados. +

+
+{% endif %} {% endblock %} diff --git a/source/templates/section.html b/source/templates/section.html index ff4735e..50b5451 100644 --- a/source/templates/section.html +++ b/source/templates/section.html @@ -3,9 +3,9 @@ {% if request.get_full_path == "/" %}

{% if gender %} - + {% else %} - + {% endif %} {{ section }}