| 1 | import logging
|
|---|
| 2 | from datetime import date
|
|---|
| 3 |
|
|---|
| 4 | from django.contrib.postgres.indexes import GinIndex
|
|---|
| 5 | from django.contrib.postgres.search import SearchVectorField
|
|---|
| 6 | from django.core import serializers
|
|---|
| 7 | from django.db import connection, models
|
|---|
| 8 | from django.db.models.constraints import UniqueConstraint
|
|---|
| 9 | from django.db.models.signals import post_save
|
|---|
| 10 | from django.dispatch import receiver
|
|---|
| 11 | from django.utils import timezone
|
|---|
| 12 | from django.utils.translation import gettext as _
|
|---|
| 13 | from mpublico.choices import *
|
|---|
| 14 |
|
|---|
| 15 | from . import helpers
|
|---|
| 16 | from .stop_words import STOP_WORDS
|
|---|
| 17 | logger = logging.getLogger(__name__)
|
|---|
| 18 |
|
|---|
| 19 | class Empresa(models.Model):
|
|---|
| 20 | created_at = models.DateTimeField(
|
|---|
| 21 | _('Fecha'), default=timezone.now, blank=True)
|
|---|
| 22 | updated_at = models.DateTimeField(auto_now=True, null=True)
|
|---|
| 23 | CodigoEmpresa = models.CharField(_('Código empresa'), unique=True)
|
|---|
| 24 | NombreEmpresa = models.CharField(_('Razón social'), max_length=1000)
|
|---|
| 25 |
|
|---|
| 26 | class Meta:
|
|---|
| 27 | verbose_name = 'Empresa'
|
|---|
| 28 | verbose_name_plural = 'Empresa'
|
|---|
| 29 |
|
|---|
| 30 | class Meta:
|
|---|
| 31 | indexes = [
|
|---|
| 32 | models.Index(fields=['NombreEmpresa', 'CodigoEmpresa']),
|
|---|
| 33 | models.Index(fields=['NombreEmpresa']),
|
|---|
| 34 | ]
|
|---|
| 35 |
|
|---|
| 36 | def __str__(self):
|
|---|
| 37 | return self.NombreEmpresa
|
|---|
| 38 |
|
|---|
| 39 | class Licitacion(models.Model):
|
|---|
| 40 | created_at = models.DateTimeField(
|
|---|
| 41 | _('Fecha'), default=timezone.now, blank=True)
|
|---|
| 42 | updated_at = models.DateTimeField(auto_now=True, null=True)
|
|---|
| 43 | CodigoExterno = models.CharField(_('Código externo'), unique=True)
|
|---|
| 44 | CodigoEstado = models.IntegerField(
|
|---|
| 45 | _('Código estado'), choices=ESTADO_LICITACION_CHOICES, blank=True, null=True)
|
|---|
| 46 | FechaCierre = models.DateTimeField(
|
|---|
| 47 | _('Fecha Cierre'), blank=True, null=True)
|
|---|
| 48 | Nombre = models.CharField(_('Nombre Producto'), blank=True, null=True)
|
|---|
| 49 | date = models.DateField(_("Fecha Publicación"), blank=True, null=True)
|
|---|
| 50 | empresa = models.ForeignKey(
|
|---|
| 51 | Empresa, on_delete=models.CASCADE, blank=True, null=True)
|
|---|
| 52 |
|
|---|
| 53 | class Meta:
|
|---|
| 54 | verbose_name = 'Licitacion Base'
|
|---|
| 55 | verbose_name_plural = 'Licitacion Base'
|
|---|
| 56 |
|
|---|
| 57 | def __str__(self):
|
|---|
| 58 | return self.CodigoExterno
|
|---|
| 59 |
|
|---|
| 60 | class LicitacionDetalle(models.Model):
|
|---|
| 61 | licitacion = models.ForeignKey(
|
|---|
| 62 | Licitacion, on_delete=models.CASCADE, related_name='licitacion_detalle')
|
|---|
| 63 | created_at = models.DateTimeField(
|
|---|
| 64 | _('Fecha'), default=timezone.now, blank=True)
|
|---|
| 65 | updated_at = models.DateTimeField(auto_now=True, null=True)
|
|---|
| 66 | active = models.BooleanField(default=True)
|
|---|
| 67 | CodigoExterno = models.CharField()
|
|---|
| 68 | CodigoEstado = models.IntegerField(
|
|---|
| 69 | _('Código estado'), choices=ESTADO_LICITACION_CHOICES)
|
|---|
| 70 | Descripcion = models.CharField(
|
|---|
| 71 | _('Descripción de la Licitación u objetode la contratación'), null=True, blank=True)
|
|---|
| 72 | Estado = models.CharField(
|
|---|
| 73 | _('Estado en el que se encuentra la Licitación'), null=True, blank=True)
|
|---|
| 74 | Informada = models.BooleanField(
|
|---|
| 75 | _('Indica si la Licitación es Informada'), null=True, blank=True)
|
|---|
| 76 | DiasCierreLicitacion = models.IntegerField(
|
|---|
| 77 | _('cantidad de días para el cierre de la Licitación'), null=True, blank=True)
|
|---|
| 78 | CodigoTipo = models.IntegerField(
|
|---|
| 79 | _('Tipo Licitación'), choices=CODIGO_TIPO_LICITACION_CHOICES, null=True, blank=True)
|
|---|
| 80 | Tipo = models.CharField(
|
|---|
| 81 | _('Tipo de Licitación de Mercado Publico'), choices=TIPO_LICITACION_CHOICES, null=True, blank=True)
|
|---|
| 82 | TipoConvocatoria = models.IntegerField(
|
|---|
| 83 | _('Tipo Convocatoria'), choices=TIPO_CONVOCATORIA_CHOICES, null=True, blank=True)
|
|---|
| 84 | Moneda = models.CharField(
|
|---|
| 85 | _('Código de la moneda en la Licitación'), choices=MONEDA_CHOICES, null=True, blank=True)
|
|---|
| 86 | MontoEstimado = models.FloatField(
|
|---|
| 87 | _('Monto estimado que maneja el Organismo o Institución para licitar'), null=True, blank=True)
|
|---|
| 88 |
|
|---|
| 89 | def __str__(self):
|
|---|
| 90 | return str(self.CodigoExterno)
|
|---|
| 91 |
|
|---|
| 92 | class Meta:
|
|---|
| 93 | verbose_name = 'Licitacion Detalle'
|
|---|
| 94 | verbose_name_plural = 'Licitacion Detalle'
|
|---|
| 95 |
|
|---|
| 96 | indexes = [
|
|---|
| 97 | models.Index(
|
|---|
| 98 | fields=['MontoEstimado', 'licitacion', 'CodigoExterno', 'active']),
|
|---|
| 99 | models.Index(fields=['MontoEstimado', 'licitacion']),
|
|---|
| 100 | models.Index(fields=['MontoEstimado']),
|
|---|
| 101 | models.Index(fields=['Adjudicacion_UrlActa']),
|
|---|
| 102 | models.Index(fields=['active', 'CodigoExterno']),
|
|---|
| 103 | models.Index(fields=['active']),
|
|---|
| 104 | ]
|
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 | class Proveedor(models.Model):
|
|---|
| 108 | created_at = models.DateTimeField(
|
|---|
| 109 | _('Fecha'), default=timezone.now, blank=True)
|
|---|
| 110 | updated_at = models.DateTimeField(auto_now=True, null=True)
|
|---|
| 111 | RutProveedor = models.CharField()
|
|---|
| 112 | NombreProveedor = models.CharField()
|
|---|
| 113 |
|
|---|
| 114 | def __str__(self):
|
|---|
| 115 | return self.NombreProveedor
|
|---|
| 116 |
|
|---|
| 117 | class Meta:
|
|---|
| 118 | constraints = [
|
|---|
| 119 | models.UniqueConstraint(
|
|---|
| 120 | fields=['RutProveedor', 'NombreProveedor'], name='unique_proovedor')
|
|---|
| 121 | ]
|
|---|
| 122 | verbose_name = 'Proveedor'
|
|---|
| 123 | verbose_name_plural = 'Proveedor'
|
|---|
| 124 |
|
|---|
| 125 | indexes = [
|
|---|
| 126 | models.Index(fields=['RutProveedor', 'NombreProveedor']),
|
|---|
| 127 | models.Index(fields=['NombreProveedor']),
|
|---|
| 128 | ]
|
|---|
| 129 |
|
|---|
| 130 |
|
|---|
| 131 | class LicitacionItem(models.Model):
|
|---|
| 132 | created_at = models.DateTimeField(
|
|---|
| 133 | _('Fecha'), default=timezone.now, blank=True)
|
|---|
| 134 | updated_at = models.DateTimeField(auto_now=True, null=True)
|
|---|
| 135 | licitacion = models.ForeignKey(
|
|---|
| 136 | Licitacion, on_delete=models.CASCADE, related_name='licitacion_items')
|
|---|
| 137 | licitacion_detalle = models.ForeignKey(
|
|---|
| 138 | LicitacionDetalle, on_delete=models.CASCADE, related_name='licitacion_detalle_items')
|
|---|
| 139 | producto = models.ForeignKey(
|
|---|
| 140 | 'Producto', on_delete=models.CASCADE, blank=True, null=True)
|
|---|
| 141 | categoria = models.ForeignKey(
|
|---|
| 142 | 'Categoria', on_delete=models.CASCADE, blank=True, null=True)
|
|---|
| 143 | proveedor = models.ForeignKey(
|
|---|
| 144 | Proveedor, on_delete=models.CASCADE, null=True, blank=True)
|
|---|
| 145 | CodigoExterno = models.CharField()
|
|---|
| 146 | Descripcion = models.CharField(blank=True, null=True)
|
|---|
| 147 | UnidadMedida = models.CharField(_('Unidad Medida'), blank=True, null=True)
|
|---|
| 148 | Cantidad = models.FloatField(_('Cantidad Licitada'), blank=True, null=True)
|
|---|
| 149 | Adjudicacion_Cantidad = models.FloatField(
|
|---|
| 150 | _('Cantidad Adjudicada'), blank=True, null=True)
|
|---|
| 151 | Adjudicacion_MontoUnitario = models.FloatField(
|
|---|
| 152 | _('Monto Unitario Adjudicación'), blank=True, null=True)
|
|---|
| 153 | active = models.BooleanField(_('Vigencia Item'), default=True)
|
|---|
| 154 | Correlativo = models.IntegerField(blank=True, null=True)
|
|---|
| 155 | analizado = models.BooleanField(default=False)
|
|---|
| 156 | touch = models.BooleanField(default=False)
|
|---|
| 157 | search = SearchVectorField(
|
|---|
| 158 | null=True,
|
|---|
| 159 | help_text="used to store all searchable info",
|
|---|
| 160 | )
|
|---|
| 161 |
|
|---|
| 162 | def __str__(self):
|
|---|
| 163 | return self.Descripcion
|
|---|
| 164 |
|
|---|
| 165 | class Meta:
|
|---|
| 166 | verbose_name = 'Licitacion Items Seguimiento Diario'
|
|---|
| 167 | verbose_name_plural = 'Licitacion Items Seguimiento Diario'
|
|---|
| 168 | indexes = [
|
|---|
| 169 | models.Index(fields=['Descripcion', 'active', 'Cantidad']),
|
|---|
| 170 | models.Index(fields=['Cantidad', 'licitacion']),
|
|---|
| 171 | models.Index(fields=['active', 'CodigoExterno']),
|
|---|
| 172 | models.Index(fields=['Descripcion', 'licitacion']),
|
|---|
| 173 | models.Index(fields=['Cantidad']),
|
|---|
| 174 | models.Index(fields=['Descripcion']),
|
|---|
| 175 | models.Index(fields=['active']),
|
|---|
| 176 | GinIndex(fields=["search"]),
|
|---|
| 177 | ]
|
|---|
| 178 |
|
|---|
| 179 | def update_search_vector(self):
|
|---|
| 180 | """
|
|---|
| 181 | Update SearchVector field of SearchModel using raw SQL
|
|---|
| 182 | search field is used to store SearchVector
|
|---|
| 183 | """
|
|---|
| 184 | db_table = self._meta.db_table
|
|---|
| 185 |
|
|---|
| 186 | # first get anything interesting out of the media
|
|---|
| 187 | # that needs to be searchable
|
|---|
| 188 |
|
|---|
| 189 | if self.id:
|
|---|
| 190 |
|
|---|
| 191 | items = []
|
|---|
| 192 | if self.Descripcion:
|
|---|
| 193 | items.append(str(self.Descripcion))
|
|---|
| 194 |
|
|---|
| 195 | if self.proveedor:
|
|---|
| 196 | if self.proveedor.NombreProveedor:
|
|---|
| 197 | items.append(str(self.proveedor.NombreProveedor))
|
|---|
| 198 |
|
|---|
| 199 | if self.categoria:
|
|---|
| 200 | if self.categoria.Categoria:
|
|---|
| 201 | items.append(str(self.categoria.Categoria))
|
|---|
| 202 |
|
|---|
| 203 | if self.licitacion:
|
|---|
| 204 | if self.licitacion.Nombre:
|
|---|
| 205 | items.append(str(self.licitacion.Nombre))
|
|---|
| 206 |
|
|---|
| 207 | items = [item for item in items if item]
|
|---|
| 208 | text = " ".join(items)
|
|---|
| 209 | text = " ".join([token for token in text.lower().split(
|
|---|
| 210 | " ") if token not in STOP_WORDS])
|
|---|
| 211 |
|
|---|
| 212 | text = helpers.clean_query(text)
|
|---|
| 213 |
|
|---|
| 214 | sql_code = """
|
|---|
| 215 | UPDATE {db_table} SET search = to_tsvector(
|
|---|
| 216 | '{config}', '{text}'
|
|---|
| 217 | ) WHERE {db_table}.id = {id}
|
|---|
| 218 | """.format(
|
|---|
| 219 | db_table=db_table, config="simple", text=text, id=self.id
|
|---|
| 220 | )
|
|---|
| 221 |
|
|---|
| 222 | try:
|
|---|
| 223 | with connection.cursor() as cursor:
|
|---|
| 224 | cursor.execute(sql_code)
|
|---|
| 225 | except BaseException as e:
|
|---|
| 226 | logger.error(f': LicitacionItem {str(e)}')
|
|---|
| 227 | pass
|
|---|
| 228 | return True
|
|---|
| 229 |
|
|---|
| 230 | class Categoria(models.Model):
|
|---|
| 231 | created_at = models.DateTimeField(
|
|---|
| 232 | _('Fecha'), default=timezone.now, blank=True)
|
|---|
| 233 | updated_at = models.DateTimeField(auto_now=True, null=True)
|
|---|
| 234 | Categoria = models.CharField()
|
|---|
| 235 | CodigoCategoria = models.CharField()
|
|---|
| 236 |
|
|---|
| 237 | def __str__(self):
|
|---|
| 238 | return str(self.Categoria)
|
|---|
| 239 |
|
|---|
| 240 | class Meta:
|
|---|
| 241 | constraints = [
|
|---|
| 242 | models.UniqueConstraint(
|
|---|
| 243 | fields=['Categoria', 'CodigoCategoria'], name='unique_categoria')
|
|---|
| 244 | ]
|
|---|
| 245 | verbose_name = 'Categoria'
|
|---|
| 246 | verbose_name_plural = 'Categoria'
|
|---|
| 247 |
|
|---|
| 248 | indexes = [
|
|---|
| 249 | models.Index(fields=['CodigoCategoria', 'Categoria']),
|
|---|
| 250 | models.Index(fields=['Categoria']),
|
|---|
| 251 | ]
|
|---|
| 252 |
|
|---|
| 253 | class Producto(models.Model):
|
|---|
| 254 | created_at = models.DateTimeField(
|
|---|
| 255 | _('Fecha'), default=timezone.now, blank=True)
|
|---|
| 256 | updated_at = models.DateTimeField(auto_now=True, null=True)
|
|---|
| 257 | NombreProducto = models.CharField()
|
|---|
| 258 | CodigoProducto = models.CharField()
|
|---|
| 259 | categoria = models.ForeignKey(
|
|---|
| 260 | Categoria, on_delete=models.CASCADE, blank=True, null=True)
|
|---|
| 261 |
|
|---|
| 262 | def __str__(self):
|
|---|
| 263 | return self.NombreProducto
|
|---|
| 264 |
|
|---|
| 265 | class Meta:
|
|---|
| 266 | constraints = [
|
|---|
| 267 | models.UniqueConstraint(
|
|---|
| 268 | fields=['NombreProducto', 'CodigoProducto'], name='unique_producto')
|
|---|
| 269 | ]
|
|---|
| 270 |
|
|---|
| 271 | verbose_name = 'Producto'
|
|---|
| 272 | verbose_name_plural = 'Producto'
|
|---|
| 273 |
|
|---|
| 274 | indexes = [
|
|---|
| 275 | models.Index(fields=['NombreProducto',
|
|---|
| 276 | 'categoria', 'CodigoProducto']),
|
|---|
| 277 | models.Index(fields=['NombreProducto', 'categoria']),
|
|---|
| 278 | models.Index(fields=['NombreProducto']),
|
|---|
| 279 | ]
|
|---|