class CallRequestAdmin(admin.ModelAdmin): list_display = ('id', 'time', 'get_user1', 'user2_link', 'transaction_display', 'get_object_display', 'name', 'email', 'phone', 'traffic_source') raw_id_fields = ('user1', 'user2', 'transaction') list_filter = (RequestTypeFilter, PaidRequestFilter, make_datetime_filter('time'), CallRequestIPAddressFilter, 'traffic_source', make_userprovince_filter('user2__region'), TransactionSumFilter) date_hierarchy = 'time' def get_queryset(self, request): return super(CallRequestAdmin, self).get_queryset(request)\ .prefetch_related('transaction', 'user2__realtors__agency') def get_user1(self, obj): return u' '.join(map(unicode, filter(None, [obj.user1, obj.ip]))) get_user1.allow_tags = True get_user1.short_description = u'отравитель' get_user1.admin_order_field = u'user1' def user2_link(self, obj): return get_user_filter_link(obj.user2, 'user2__exact') user2_link.allow_tags = True user2_link.short_description = u'получатель' user2_link.admin_order_field = u'user2' def get_object_display(self, obj): model_name = obj.content_type.model obj_type = { 'ad': u'Объявл', 'newhome': u'Новостр', 'savedsearch': u'Поиск' }.get(model_name, 'прочее') if obj.object: if model_name in ['ad', 'newhome']: return '%s: <a href="%s">%s</a>' % ( obj_type, obj.object.get_absolute_url(), obj.object) elif model_name == 'savedsearch': return '%s: <a href="%s">%s</a>' % ( obj_type, obj.object.get_full_url(), obj.object) return '%s: %s' % (obj_type, obj.object) get_object_display.allow_tags = True get_object_display.short_description = u'объект' def transaction_display(self, obj): if obj.transaction: if obj.transaction.amount == 0: return u'бонус' else: return u'%s грн' % int(-obj.transaction.amount) transaction_display.short_description = u'цена'
class CallAdmin(admin.ModelAdmin): list_display = ('id', 'call_time', 'callerid1', 'callerid2', 'user2_link', 'transaction_display', 'deal_type', 'complaint', 'recordingfile_link') list_filter = ('complaint', 'deal_type', PaidCallFilter, make_datetime_filter('call_time'), CallProxyNumberFilter, 'proxynumber__mobile_operator', 'proxynumber__traffic_source', make_userprovince_filter('user2__region'), TransactionSumFilter) raw_id_fields = ('user1', 'user2', 'transaction') date_hierarchy = 'call_time' actions = ('cancel', ) def get_queryset(self, request): return super(CallAdmin, self).get_queryset(request)\ .prefetch_related('user2__realtors__agency', 'user2__leadgenerationbonuses', 'transaction') def cancel(self, request, queryset): for call in queryset: call.cancel(request.user) cancel.short_description = u'Возврат денег за звонок' def user2_link(self, obj): return get_user_filter_link(obj.user2, 'user2__exact') user2_link.allow_tags = True user2_link.short_description = u'пользователь' def recordingfile_link(self, obj): if obj.duration: duration_str = u'%.1f мин' % (obj.duration / 60.0) else: duration_str = u'' if obj.recordingfile: return u'<a href="%s">%s</a>' % (obj.recordingfile.url, duration_str) else: return duration_str recordingfile_link.allow_tags = True recordingfile_link.short_description = u'Запись' def transaction_display(self, obj): if obj.transaction: if obj.transaction.amount == 0: for bonus in obj.user2.leadgenerationbonuses.all(): if bonus.start < obj.call_time and ( not bonus.end or obj.call_time < bonus.end): return u'бонус' return u'0 грн' else: return u'%s грн' % int(-obj.transaction.amount) transaction_display.short_description = u'цена'
class SearchCounterAdmin(admin.ModelAdmin): raw_id_fields = ['region'] list_display = [ 'date', 'deal_type', 'property_type', 'rooms', 'region', 'price_from', 'price_to', 'currency', 'area_from', 'area_to', 'facilities', 'without_commission', 'other_parameters', 'searches_first_page', 'searches_all_pages' ] list_filter = ( make_datetime_filter('date'), 'deal_type', 'property_type', fs.RoomsFilter, 'without_commission', 'currency', fs.PriceFromFilter, fs.PriceToFilter, fs.AreaFromFilter, fs.AreaToFilter, fs.FacilitiesFilter, fs.ProvinceRegionFilter, fs.CityRegionFilter, fs.DistrictFilter, )
class StatAdmin(admin.ModelAdmin): raw_id_fields = ('user', ) list_display = ('display_user', 'user_email', 'new_properties', 'active_properties', 'paid_properties', 'money_spent', 'entrances', 'ad_views', 'ad_contacts_views', 'plan_first', 'plan_other', 'time_period') list_filter = [ StatManagerFilter, make_datetime_filter('date'), AgencyFilter, StatDataFilter, StatByProvince ] search_fields = ( 'user__email', 'user__agencies__name', 'user__first_name', 'user__last_name', ) date_hierarchy = 'date' list_display_links = None def get_queryset(self, request): return super(StatAdmin, self).get_queryset(request).prefetch_related( 'user', 'user__agencies') def display_user(self, obj): if obj.user: return get_user_filter_link(obj.user) else: return u'все пользователи' display_user.allow_tags = True display_user.short_description = u'Пользователь' display_user.admin_order_field = 'user' def user_email(self, obj): if obj.user: return obj.user.email user_email.short_description = u"E-mail" def time_period(self, obj): return obj.date.strftime('%Y.%m.%d') time_period.short_description = u"День" time_period.admin_order_field = 'date'
class ActivityPeriodAdmin(admin.ModelAdmin): list_display = ('id', 'lead_type', 'user_link', 'start', 'display_status', 'get_calls', 'get_requests', 'get_numbers') list_filter = ('lead_type', IsActiveFilter, make_datetime_filter('start'), make_datetime_filter('end'), NumberTypeFilter, StatManagerFilter, make_userprovince_filter('user__region')) raw_id_fields = ('user', ) def get_queryset(self, request): return super(ActivityPeriodAdmin, self).get_queryset(request)\ .prefetch_related('user__callrequests_to', 'user__proxynumbers', 'user__answered_ppc_calls', 'user__realtors__agency', 'user__transactions', 'user__leadgeneration') def get_calls(self, obj): if obj.end: return obj.calls else: return len([ call for call in obj.user.answered_ppc_calls.all() if call.call_time > obj.start ]) get_calls.short_description = u'звонков' def get_requests(self, obj): if obj.end: return obj.requests else: return len([ callrequest for callrequest in obj.user.callrequests_to.all() if callrequest.time > obj.start ]) get_requests.short_description = u'запросов' def get_numbers(self, obj): if obj.end: return obj.numbers else: return obj.user.proxynumbers.count() get_numbers.short_description = u'телефонов' def user_link(self, obj): return get_user_filter_link(obj.user) user_link.allow_tags = True user_link.short_description = u'пользователь' def display_status(self, obj): if obj.end: return u"завершено %s" % obj.end.strftime("%Y-%m-%d %H:%M") else: result = [u"мобильные"] if not obj.user.leadgeneration.dedicated_numbers: result.append(u"общий") else: result.append( u"выделенный" if obj.user.has_paid_dedicated_numbers( ) else u"нет оплаты за выделенный") balance = int(sum([t.amount for t in obj.user.transactions.all()])) result.append( u'баланс <a title="Показать транзакции пользователя" href="%s?user__exact=%d">%s грн</a>' % (reverse('admin:paid_services_transaction_changelist'), obj.user.id, balance)) return u" / ".join(result) display_status.allow_tags = True display_status.short_description = u'Статус'
class AdAdmin(admin.ModelAdmin): form = AdAdminForm list_display = ( 'short_name', 'user_link', 'get_status_display', 'type', 'region_text', 'address', 'price_currency', 'add_date', 'updated_date', 'item_images' ) fieldsets = ( (None, { 'classes': ('wide_label',), 'fields': (('user_info', 'bank'), ('status', 'moderation_status', 'vip_type'), ('deal_type', 'property_type', 'property_commercial_type')) }), (u'Основные параметры', { 'fields': ('title', 'address', ('addr_city', 'addr_street', 'addr_house', 'addr_country'), ('rooms', 'area', 'area_living', 'area_kitchen'), 'description', ('price', 'currency'), 'iframe_url') }), (u'Дополнительные параметры', { 'fields': ( ('floor', 'floors_total'), ('building_layout', 'building_type', 'building_type_other'), ('building_walls', 'building_windows', 'building_heating'), 'guests_limit',) }), (u'Прочее', { 'classes': ('collapse',), 'fields': (('created', 'modified'), ('updated', 'expired'), ('reserved', 'modified_calendar'), ('price_period', 'space_units'), 'facilities', 'contact_person', ('detail_views', 'contacts_views'), ('coords_x', 'coords_y', 'geo_source'), ) }), ) readonly_fields = ('title', 'contact_person', 'updated', 'expired', 'user_info', 'address', 'modified', 'modified_calendar') list_filter = ( fs.PropertyById, fs.M2MPhoneFilter, make_datetime_filter('created'), make_datetime_filter('updated'), fs.PropertyWithModeration, fs.PropertyBySource, fs.PropertyByContentProvider, 'status', 'moderation_status', 'vip_type', fs.CityRegionFilter, fs.ProvinceRegionFilter, 'deal_type', 'property_type' ) filter_horizontal = ['facilities', 'rules'] exclude = ['image', 'region', 'price_uah', 'xml_id'] search_fields = ['id'] show_full_result_count = False # date_hierarchy = 'created' actions = [update_region, approve_ads, to_rentdaily_ad] + change_status_actions + wrong_ad_actions + [ban_ad] inlines = [ PhoneInline, PhotoInline, ] # custom field in fieldsets def user_info(self, obj): if obj.user: return u'ID %d (%s)' % (obj.user_id, obj.user.email or u'нет e-mail') user_info.short_description = u'пользователь' def get_readonly_fields(self, request, obj=None): readonly_fields = self.readonly_fields if not request.user.has_perm("%s.%s_%s" % ( 'ad', 'change', 'moderation')): readonly_fields = readonly_fields + ('status', 'moderation_status') if not request.user.has_perm("%s.%s_%s" % ( 'bank', 'change', 'bank')): readonly_fields = readonly_fields + ('vip_type', ) if not request.user.is_superuser: readonly_fields = readonly_fields + ('addr_country',) # шиш вам if request.user.groups.filter(name__icontains=u'комитет').exists(): return list(set( list(readonly_fields) + ['facilities'] + [field.name for field in self.opts.local_fields] + [field.name for field in self.opts.local_many_to_many] )) return readonly_fields def get_actions(self, request): actions = super(AdAdmin, self).get_actions(request) if not request.user.has_perm("%s.%s_%s" % ( 'ad', 'change', 'moderation')): return [] return actions @property def media(self): return super(AdAdmin, self).media + forms.Media(js=['js/libs/jquery-last.min.js', 'js/admin_changeform.js', ]) def get_queryset(self, request): qs = super(AdAdmin, self).get_queryset(request) return qs.select_related('region').prefetch_related('photos', 'bank', 'user', 'moderations', 'catalogplacements', 'user__user_plans', 'user__activityperiods', 'user__realtors__agency', 'user__leadgeneration') def get_status_display(self, obj): if not obj.moderation_status: status = obj.get_status_display() if obj.status == 1: for deactivation in obj.deactivations.all(): if not deactivation.returning_time: status = u'<a href="%s?basead=%d">продано</a>' % (reverse('admin:ad_deactivationforsale_changelist'), obj.id) break if obj.user: for placement in obj.catalogplacements.all(): if placement.until > datetime.datetime.now(): status += u' <a href="{}?basead={}" title="размещение в зарубежном каталоге"><img src="{}" style="background-color:{}"/></a> '.format( reverse('admin:paid_services_catalogplacement_changelist'), obj.id, static('admin/img/icon-dollar.png'), '#6c6874') if obj.addr_country == 'UA': active_plan = obj.user.get_active_plan_using_prefetch() if active_plan: status += u' <a href="{}?id={}" title="{}"><img src="{}" style="background-color:{}"/></a> '.format( reverse('admin:paid_services_userplan_changelist'), active_plan.id, u'Тариф до {:%d.%m.%Y}. Лимит объявлений - {}'.format(active_plan.end, active_plan.ads_limit), static('admin/img/icon-dollar.png'), '#ecb310') if obj.user.has_active_leadgeneration('ads'): status += u' <a href="{}?id={}" title="лидогенерация"><img src="{}" style="background-color:{}"/></a> '.format( reverse('admin:ppc_leadgeneration_changelist'), obj.user.leadgeneration.id, static('admin/img/icon-dollar.png'), '#31c500') return status else: status = obj.get_moderation_status_display() return '<b class="red">%s</b>' % status[:18] + '..' if len(status) > 18 else status get_status_display.allow_tags = True get_status_display.short_description = u"статус" get_status_display.admin_order_field = 'status' def region_text(self, obj): if obj.region is None: return u'(без региона)' else: return u'<a href="?region__id__exact=%d" title="все объявления региона \'%s\'">%s</a>' % (obj.region.pk, obj.region.text, obj.region) region_text.allow_tags = True region_text.short_description = u"присв.\nрегион" region_text.admin_order_field = 'region' def short_name(self, obj): name = obj.title if obj.xml_id: name += " (#%s)" % obj.xml_id if len(name) > 25: name = "<span title='%s'>%s...</span>" % (name, name[:25]) if obj.fields_for_moderation: active_moderations = filter(lambda moderation: not moderation.moderator_id, obj.moderations.all()) if active_moderations: name += u' <a href="%s"><img src="%s" title="Модерация: %s"/></a>' % \ (reverse('moderation_detail', args=[active_moderations[0].id]), static('admin/img/icon-moderate.gif'), obj.fields_for_moderation.replace("__all__", u"Новое")) return name short_name.allow_tags = True short_name.short_description = u"объявление" short_name.admin_order_field = 'title' def price_currency(self, obj): if obj.currency != 'UAH': return u'<acronym title="%s грн.">%s %s</acronym>' % (obj.price_uah, obj.price, obj.get_currency_display()) else: return u'%s %s' % (obj.price, obj.get_currency_display()) price_currency.allow_tags = True price_currency.short_description = u"Цена" price_currency.admin_order_field = 'price_uah' def add_date(self, obj): return obj.created.strftime('%d.%m.%y %H:%M') add_date.short_description = u"Добавлено" add_date.admin_order_field = 'created' def updated_date(self, obj): return obj.updated.strftime('%d.%m.%y %H:%M') updated_date.short_description = u"Обновлено" updated_date.admin_order_field = 'updated' def type(self,obj): return u'%s, %s' % (obj.get_deal_type_display(), obj.get_property_type_display()) type.short_description = u"Тип" type.admin_order_field = 'deal_type' def user_link(self, obj): if obj.user: html = get_user_filter_link(obj.user) if obj.bank: url = reverse('admin:ad_ad_changelist') + '?bank_id=%d' % obj.bank_id html += u'<br/>банк: <a href="%s">%s</a>' % (url, obj.bank.name) return html else: if obj.source_url: o = urlparse(obj.source_url) return u'<nobr><a href="?source_url__icontains=%s" title="фильтровать по адресу сайта">%s %s</a></nobr>' % (o.netloc, icon_website, o.netloc) elif obj.content_provider: return obj.get_content_provider_display() user_link.allow_tags = True user_link.short_description = u"Источник/Юзер" user_link.admin_order_field = 'source_url' def item_images(self, obj): image_count = obj.photos.all().count() return u'<a href="%s?basead__exact=%d">%s шт.</a>' % (reverse('admin:ad_photo_changelist'), obj.pk, image_count) item_images.allow_tags = True item_images.short_description = u"Фото" def save_model(self, request, obj, form, change): if request.user.has_perm("%s.%s_%s" % ( 'ad', 'change', 'moderation')): # после сохранения в админке очищаются поля, требующие модерации obj.fields_for_moderation = None # ..., а так же закрываются заявки на модерацию update_fields = {'moderator':request.user, 'end_time':datetime.datetime.now()} if 'status' in obj.get_dirty_fields(): update_fields['new_status'] = obj.status obj.moderations.filter(moderator__isnull=True).update(**update_fields) if obj.id is None and obj.user is None: obj.user = request.user super(AdAdmin, self).save_model(request, obj, form, change) if request.user.has_perm('ad.change_reserveddate') and getattr(form, 'update_m2m_reservations', False): obj.update_reserved_from_json(form.cleaned_data['reserved']) def get_urls(self): urls = super(AdAdmin, self).get_urls() return [ url(r'^stats_by_propertytype/$', admin_views.stats_by_propertytype), url(r'^feed_sources/$', admin_views.feed_sources), url(r'^duplicates/$', admin_views.show_photo_duplicates), ] + urls
class CustomUserAdmin(UserAdmin): list_display = ( '__unicode__', 'display_info', 'id', 'display_email', 'display_phones', 'display_is_active', 'region', 'display_ads_count', 'display_date_joined', 'display_last_action', 'display_balance', 'display_ban', ) inlines = [PhoneInline] actions = [ 'move_to_managers', 'remove_from_managers', 'create_agency', 'activate', 'deactivate', 'update_phones', 'give_leadgeneration_bonus' ] search_fields = ('id', 'agencies__name', 'first_name', 'last_name') list_filter = (ManagerFilter, RegionFilter, M2MPhoneFilter, make_datetime_filter('date_joined'), 'is_active', AgencyFilter, DeveloperFilter, NewPlanFilter, make_datetime_filter('last_action'), LoyaltyFilter, BanFilter, 'is_staff', 'is_superuser', IPAddressFilter, BannedPhonesFilter, AdsSumFilter) show_full_result_count = False def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super(CustomUserAdmin, self).get_search_results( request, queryset, search_term) filter = Q() for word in search_term.split(): if '@' in word: filter.add(Q(email__iexact=word), Q.OR) if filter: queryset |= self.model.objects.filter(filter) return queryset, use_distinct def get_list_filter(self, request): list_filter = list( super(CustomUserAdmin, self).get_list_filter(request)) if not request.user.groups.filter(name__icontains=u'комитет').exists(): list_filter.insert(-1, TestPlanFilter) return list_filter def get_fieldsets(self, request, obj=None): fieldsets = list( super(CustomUserAdmin, self).get_fieldsets(request, obj)) if not request.user.is_superuser: fieldsets.pop(2) # поля с правами fieldsets.append((None, { 'fields': ('manager', 'region', 'city', 'image', 'loyalty_started', 'show_email', 'show_message_button', 'subscribe_info', 'subscribe_news', 'receive_sms', 'language', 'gender', 'ip_addresses') })) return fieldsets def get_readonly_fields(self, request, obj=None): if request.user.is_superuser: readonly_fields = [] else: readonly_fields = [ 'username', 'last_login', 'last_action', 'date_joined', 'ads_count' ] if not request.user.groups.filter(id=12).exists(): readonly_fields.append('manager') return readonly_fields def get_queryset(self, request): return super(CustomUserAdmin, self).get_queryset(request)\ .prefetch_related('leadgeneration', 'developer', 'phones', 'realtors__agency', 'region', 'transactions', 'user_plans', 'activityperiods', 'bans', 'stat_set', 'social_auth') def lookup_allowed(self, key, value): if key in ('transactions__amount', ): return True return super(CustomUserAdmin, self).lookup_allowed(key, value) def get_actions(self, request): actions = super(CustomUserAdmin, self).get_actions(request) if not request.user.has_perm('ad.change_phoneinad'): del actions['update_phones'] # group ID 12 - руководитель отдела менеджеров if not (request.user.is_superuser or request.user.groups.filter(id=12).exists()): if 'move_to_managers' in actions: del actions['move_to_managers'] if 'remove_from_managers' in actions: del actions['remove_from_managers'] return actions def display_email(self, obj): if obj.email: items = [obj.email] else: items = [] website_icon = u'<img src="%s" alt=""/>' % static( 'admin/img/icon-website.gif') for social in obj.social_auth.all(): # TODO добавить гугл+ link = None if social.provider == 'facebook': link = "http://facebook.com/%s" % social.uid elif social.provider in [ 'vkontakte-oauth2', 'vkontakte', 'vk-oauth', 'vk-oauth2' ]: link = "http://vk.com/id%s" % social.uid elif social.provider == 'twitter': link = "https://twitter.com/intent/user?user_id=%s" % social.uid if link: items.append(u'<a href="%s" target="_blank">%s %s</a>' % (link, website_icon, social.provider)) return ' '.join(items) display_email.short_description = u'E-mail/соцсеть' display_email.allow_tags = True def display_phones(self, obj): return u'<br/>'.join([p.number for p in obj.phones.all()]) display_phones.short_description = u'телефоны' display_phones.allow_tags = True def display_date_joined(self, obj): return (obj.date_joined.strftime('%d.%m.%Y') if obj.date_joined else None) display_date_joined.short_description = u'Регистрация' display_date_joined.admin_order_field = 'date_joined' def display_last_action(self, obj): return (obj.last_action.strftime('%d.%m.%Y') if obj.last_action else None) display_last_action.short_description = u'Активн.' display_last_action.admin_order_field = 'last_action' def display_ads_count(self, obj): result = u'<a href="%s?user__exact=%d">%s</a>' % ( reverse('admin:ad_ad_changelist'), obj.id, obj.ads_count or '-') newhomes_amount = obj.newhomes.all().count() if obj.is_developer( ) else 0 if newhomes_amount: result += u' / <a href="%s?user__exact=%d">%d</a>' % (reverse( 'admin:newhome_newhome_changelist'), obj.id, newhomes_amount) return result display_ads_count.allow_tags = True display_ads_count.short_description = u'Объявл' display_ads_count.admin_order_field = 'ads_count' def display_is_active(self, obj): return obj.is_active display_is_active.boolean = True display_is_active.short_description = u'Акт' display_is_active.admin_order_field = 'is_active' def get_urls(self): urls = [ url(r'^buy_plan/$', self.admin_site.admin_view(self.buy_plan), name="buy_plan"), url(r'^buy_vip/$', self.admin_site.admin_view(self.buy_vip), name="buy_vip"), url(r'^move_money/$', self.admin_site.admin_view(self.move_money), name="move_money"), url(r'^login_as/(?P<user_id>\d+)/$', self.admin_site.admin_view(self.login_as), name='login_as'), url(r'^(?P<user_id>.+)/info/$', self.admin_site.admin_view(self.info_view), name='custom_user_user_info') ] return urls + super(CustomUserAdmin, self).get_urls() def info_view(self, request, user_id): obj = User.objects.get(id=user_id) unexpired_plans = obj.get_unexpired_plans() prolonged_plans = unexpired_plans.exclude(is_active=True) leadgeneration = obj.get_leadgeneration() has_leadgeneration_ads = obj.has_active_leadgeneration('ads') recently_ppc_period = obj.activityperiods.filter( end__gt=datetime.datetime.now() - datetime.timedelta(days=7)).last() realtor = obj.get_realtor() if request.method == 'GET' and request.GET: # модераторы или руководитель отдела менеджеров if request.user.is_superuser or request.user.groups.filter( id__in=[1, 12]).exists(): if 'cancel_prolonged_plans' in request.GET: if prolonged_plans: for prolonged_plan in prolonged_plans: prolonged_plan.cancel(request.user, 'full') else: raise Exception('No prolonged plans') elif 'set_ads_limit' in request.GET or 'cancel_dedicated_numbers' in request.GET: if has_leadgeneration_ads: if 'set_ads_limit' in request.GET: leadgeneration.ads_limit = int( request.GET['set_ads_limit']) if 'cancel_dedicated_numbers' in request.GET: # TODO: добавить related_name для corrected_transaction и заменить transaction__isnull на какой-нибудь corrections__isnull for transaction in obj.transactions.filter( type=80, time__gt=datetime.datetime.now() - datetime.timedelta(days=3), transaction__isnull=True): transaction.revert( comment= u'Возврат при отмене выделенного номера. Отменил пользователь #%d' % request.user.id) leadgeneration.dedicated_numbers = False leadgeneration.save() else: raise Exception('PPC for ads isn`t active') elif 'remove_from_agency' in request.GET: if realtor: if realtor.is_admin and realtor.agency.realtors.count( ) == 1: realtor.agency.delete() else: realtor.delete() else: raise Exception('Cannot remove user from agency') elif 'restore_ppc' in request.GET: recently_ppc_period.end = None recently_ppc_period.calls = 0 recently_ppc_period.requests = 0 recently_ppc_period.numbers = 0 recently_ppc_period.save() elif 'add_to_agency' in request.GET: agency_admin = Realtor.objects.filter( user__email=request.GET['add_to_agency'], is_admin=True).first() if agency_admin: obj.realtors.all().delete() # старые связи в печь Realtor.objects.create(user=obj, agency=agency_admin.agency, is_active=True, is_admin=False) else: self.message_user(request, u'Агентство не найдено', level=messages.ERROR) elif 'move_ads_to_user' in request.GET: new_user = User.objects.get( email=request.GET['user_email']) obj.ads.update(user=new_user) # обновление счетчиков объявлений for user in [obj, new_user]: user.update_ads_count() elif 'change_manager' in request.GET: new_manager = Manager.objects.get( id=request.GET['manager_id']) obj.manager_ptr.managed_users.update(manager=new_manager) elif 'remove_developer' in request.GET: obj.developer.delete() return redirect( reverse('admin:custom_user_user_info', args=[obj.id])) else: raise PermissionDenied else: context = self.admin_site.each_context(request) context.update( obj=obj, unexpired_plans=unexpired_plans, prolonged_plans=prolonged_plans, leadgeneration=leadgeneration, recently_ppc_period=recently_ppc_period, has_leadgeneration_ads=has_leadgeneration_ads, realtor=realtor, opts=User._meta, title=u'Информация о пользователе', has_change_permission=self.has_change_permission(request, obj), managers=Manager.objects.all(), managed_users_count=obj.manager_ptr.managed_users.count() if hasattr(obj, 'manager_ptr') else 0, all_ads_count=obj.ads.count()) return render(request, 'admin/custom_user/user_info.html', context) def display_info(self, obj): result = u'' result += u'<a title="Информация о пользователе" href="%s"><img src="%s"/></a> ' % ( reverse('admin:custom_user_user_info', args=[obj.id]), static('admin/img/icon-info.png')) if obj.stat_set.exists(): result += u'<a title="Показать статистику пользователя" href="%s?user__exact=%d"><img src="%s"/></a> ' % ( reverse('admin:profile_statgrouped_changelist'), obj.id, static('admin/img/icon-stats.gif')) for realtor in obj.realtors.all(): result += u'<a href="%s?id=%d" title="%s"%s><img src="%s"/></a> ' % \ (reverse('admin:agency_agency_changelist'), realtor.agency_id, realtor.agency.name, ' style="opacity:0.3"' if not realtor.is_active else '', static('admin/img/icon-agency.gif') if realtor.is_admin else static('admin/img/icon-user.gif')) if realtor.is_admin and realtor.agency.import_url: result += u'<a title="Показать статистику импорта" href="%s?agency=%d"><img src="%s"/></a> ' % ( reverse('admin:import__importtask_changelist'), realtor.agency.id, static('admin/img/icon-feed.png')) if hasattr(obj, 'developer'): result += u'<a href="%s?user_id=%d" title="%s"><img src="%s"/></a> ' % \ (reverse('admin:newhome_developer_changelist'), obj.id, obj.developer.name, static('admin/img/icon-developer.gif')) active_plan = obj.get_active_plan_using_prefetch() if active_plan: result += u'<a href="{}?id={}" title="{}"><img src="{}" style="background-color:{}"/></a> '.format( reverse('admin:paid_services_userplan_changelist'), active_plan.id, u'Тариф до {:%d.%m.%Y}. Лимит объявлений - {}'.format( active_plan.end, active_plan.ads_limit), static('admin/img/icon-dollar.png'), '#ecb310') active_leadgeneration = obj.has_active_leadgeneration() if active_leadgeneration: active_for = filter(None, [ u'объявлений' if 'ads' in active_leadgeneration else '', u'новостроек' if 'newhomes' in active_leadgeneration else '' ]) result += u'<a href="{}?id={}" title="включена для {}"><img src="{}" style="background-color:{}"/></a> '.format( reverse('admin:ppc_leadgeneration_changelist'), obj.leadgeneration.id, u' и '.join(active_for), static('admin/img/icon-dollar.png'), '#31c500') if not obj.is_staff and obj.is_active: result += u'<a title="Войти под этим пользователем" href="%s" onClick="return confirm(\'%s\')"><img src="%s" style="opacity:0.3"/></a> ' % ( reverse('admin:login_as', args=[obj.id]), u'ВНИМАНИЕ! Произойдет выход из текущего аккаунта. ' u'Рекомендуется открывать ссылку в режиме инкогнито с последующим повторным вводом вашего логина/пароля.', static('admin/img/icon-login.svg')) return result display_info.allow_tags = True display_info.short_description = u'Инфо' def display_ban(self, obj): if obj.id in get_banned_users(): sorted_bans = sorted(obj.bans.all(), key=lambda v: v.expire_date) return u'до %s' % sorted_bans[-1].expire_date.strftime('%d.%m.%Y') else: return u'<a href="%s?user=%d" title="Отправить в бан"><img src="%s" alt=""/></a>' % ( reverse('admin:profile_ban_add'), obj.id, static('admin/img/icon-ban.png'), ) display_ban.short_description = u'Бан' display_ban.allow_tags = True def display_balance(self, obj): # попытка использовать join с таблицей транзакций и sum кладет базу, поэтому так balance = int(sum([t.amount for t in obj.transactions.all()])) return u'<a title="Показать транзакции пользователя" href="%s?user__exact=%d">%s</a>' % ( reverse('admin:paid_services_transaction_changelist'), obj.id, balance) display_balance.allow_tags = True display_balance.short_description = u'Баланс' def display_plans(self, obj): active_plan = obj.get_active_plan_using_prefetch() if active_plan: return u'<a title="Показать планы пользователя" href="%s?user__exact=%d">до %s</a>' % ( reverse('admin:paid_services_userplan_changelist'), obj.id, active_plan.end.strftime('%Y-%m-%d')) return '' display_plans.allow_tags = True display_plans.short_description = u'План' @transaction.atomic def move_to_managers(self, request, queryset): moved = [] for user in queryset: user_str = user.email or unicode(user) if hasattr(user, 'manager_ptr'): self.message_user( request, u'Ошибка: пользователь %s уже является менеджером' % user_str, level=messages.ERROR) return else: Manager.create_from_user(user) user.is_staff = True user.save() user.groups.add(Group.objects.get(id=2)) moved.append(user_str) self.message_user(request, u'Переведены в менеджеры: %s' % u', '.join(moved), level=messages.WARNING) move_to_managers.short_description = u'Перевести в менеджеры' @transaction.atomic def remove_from_managers(self, request, queryset): removed = [] for user in queryset: user_str = user.email or unicode(user) if hasattr(user, 'manager_ptr'): # запрет удаления из менеджеров, если у менеджера есть пользователи # внимание! если захочется все равно удалять, то перед удалением нужно разорвать связи - manager_ptr.managed_users.update(manager=None), # иначе из базы вместе с менеджером удалятся все его пользователи (из-за on_delete) if user.manager_ptr.managed_users.exists(): self.message_user( request, u'Ошибка: невозможно удалить менеджера %s - у него есть пользователи' % user_str, level=messages.ERROR) return else: user.manager_ptr.delete(keep_parents=True) user.is_staff = False user.save() user.groups.remove(Group.objects.get(id=2)) removed.append(user_str) else: self.message_user( request, u'Ошибка: пользователь %s не является менеджером' % user_str, level=messages.ERROR) return self.message_user(request, u'Удалены из менеджеров: %s' % u', '.join(removed), level=messages.WARNING) remove_from_managers.short_description = u'Удалить из менеджеров' @transaction.atomic def create_agency(self, request, queryset): for user in queryset: user.create_agency() self.message_user(request, u'Созданы агентства для пользователей: %s' % [unicode(user) for user in queryset], level=messages.INFO) create_agency.short_description = u'Создать агентство' def activate(self, request, queryset): for user in queryset: user.is_active = True user.save() activate.short_description = u'Активировать' def update_phones(self, request, queryset): for user in queryset: numbers = user.phones.order_by('users_m2m').values_list('number', flat=True) user.update_ads_phones(numbers) update_phones.short_description = u'Заменить телефоны в объявлениях телефонами из профиля' def deactivate(self, request, queryset): for user in queryset: user.is_active = False user.save() deactivate.short_description = u'Деактивировать' def give_leadgeneration_bonus(self, request, queryset): from ppc.models import LeadGeneration, Bonus, ActivityPeriod for user in queryset: leadgeneration, created = LeadGeneration.objects.get_or_create( user=user) leadgeneration.is_active_ads = True leadgeneration.weekdays = [] leadgeneration.save() bonus = Bonus.objects.create(user=user, start=datetime.datetime.now()) ActivityPeriod.objects.get_or_create(user=user, end=None, lead_type='ads') give_leadgeneration_bonus.short_description = u'Включить лидогенерацию (объявл.) с 10 бесплатными звонками/лидами' def login_as(self, request, user_id=None): from django.contrib.auth import login, logout user = User.objects.get(pk=user_id) if request.user.is_superuser or ( not user.is_staff and request.user.groups.filter(id__in=[1, 2, 12]).exists()): logout(request) user.backend = 'django.contrib.auth.backends.ModelBackend' login(request, user) return redirect('/') else: self.message_user(request, u'Недостаточно прав.', level=messages.ERROR) return redirect( request.META.get('HTTP_REFERER', '/admin/custom_user/user/')) def move_money(self, request): class Form(forms.Form): from_user = UserField(label=u'От кого', help_text=u'e-mail или ID') to_user = UserField(label=u'Кому', help_text=u'e-mail или ID') amount = forms.IntegerField(label=u'Сумма перевода', min_value=1) def clean(self): if self.cleaned_data['from_user'].get_balance( force=True) < self.cleaned_data['amount']: raise forms.ValidationError( u'Недостаточно средств у пользователя %s' % self.cleaned_data['from_user']) if request.POST: from ad.models import Ad from paid_services.models import VipPlacement, InsufficientFundsError form = Form(request.POST) if form.is_valid(): from_user = form.cleaned_data['from_user'] to_user = form.cleaned_data['to_user'] if request.user.is_superuser or request.user.groups.filter(id__in=[1, 12]).exists()\ or (from_user.get_agency() and from_user.get_agency() == to_user.get_agency()): Transaction.move_money(from_user, to_user, form.cleaned_data['amount'], u'перевел %s' % request.user) self.message_user( request, u'Деньги переведены. Баланс пользователя %s: %d грн' % (to_user, to_user.get_balance(force=True)), level=messages.SUCCESS) return redirect(request.POST.get('next', '.')) else: self.message_user( request, u'Пользователи не находятся в одном агентстве', level=messages.ERROR) else: form = Form() return render(request, 'admin/move_money.html', locals()) def buy_vip(self, request): vip_discounts_choices = ( (0, 'без скидки'), (10, 'от 10 до 20 объяв - 10%'), (18, 'от 21 до 50 объяв - 18%'), (26, 'от 51 объяв - 26%'), ) weeks_choices = ( (1, 1), (2, 2), (4, 4), ) class Form(forms.Form): ids = forms.CharField(label=u'ID объявлений', widget=forms.Textarea(attrs={'rows': 4})) discount = forms.IntegerField( label=u'Скидка по объему', widget=forms.Select(choices=vip_discounts_choices)) weeks = forms.IntegerField( label=u'Длительность, недель', widget=forms.Select(choices=weeks_choices)) bonus = forms.BooleanField(label=u'Бонус', required=False) if request.POST: from ad.models import Ad from paid_services.models import VipPlacement, InsufficientFundsError now = datetime.datetime.now() form = Form(request.POST) if form.is_valid(): ad_ids = form.cleaned_data['ids'].replace(',', ' ').split() ads = Ad.objects.filter(pk__in=ad_ids) discount = form.cleaned_data['discount'] weeks = form.cleaned_data['weeks'] sum = 0 bonus_comment = u'бонус от менеджера #%d %s' % ( request.user.id, request.user.email) bonus_vips_in_month = Transaction.objects.filter( type=53, time__year=now.year, time__month=now.month, comment=bonus_comment).count() for ad in ads: user = ad.user price = user.get_paidplacement_price( ad, 'vip') * weeks * (100 - discount) / 100 transaction = Transaction(user=user, type=53, amount=-price) # бесплатные ВИПы, один менеджер может дарить не большее 30 випов в месяц if form.cleaned_data['bonus']: if bonus_vips_in_month < 30: transaction.comment = bonus_comment transaction.amount = price = 0 bonus_vips_in_month += 1 weeks = 1 sum += price message = u'[%s, объявление #%s] VIP на %d дней, стоимость %d грн - ' % ( user, ad.pk, weeks * 7, price) if ad.vip_type: self.message_user(request, u'%s уже VIP' % message, level=messages.ERROR) continue if 'buy' in request.POST: try: if price > user.get_balance(force=True): agency_admin = user.get_realtor( ).agency.get_admin_user() Transaction.move_money( agency_admin, user, price, u'покупка VIP для объявления #%s' % ad.id) message += u'(перевод денег с главного аккаунта)' transaction.save() except InsufficientFundsError: self.message_user( request, u'%s недостаточно средств. Баланс %s' % (message, user.get_balance()), level=messages.ERROR) else: vip = VipPlacement.objects.create( basead=ad, days=weeks * 7, transaction=transaction) self.message_user(request, u'%s КУПЛЕН' % message, level=messages.SUCCESS) else: self.message_user(request, u'%s ОЦЕНЕН, еще не куплен' % message, level=messages.WARNING) if not ads: self.message_user(request, u'Объявления не найдены', level=messages.ERROR) if len(ads) > 1: self.message_user(request, u'Итого по всем объявлениям: %s грн' % sum, level=messages.WARNING) else: form = Form() return render(request, 'admin/buy_vip.html', locals()) def buy_plan(self, request): class Form(forms.Form): user = UserField(label=u'Пользователь', help_text=u'email или ID') ads_limit = forms.IntegerField(label=u'Лимит по плану', min_value=1) price = forms.IntegerField(label=u'Цена тарифного плана', min_value=1, required=False) stop_active_plan = forms.BooleanField( label=u'Останавливать текущий', required=False) if request.POST: form = Form(request.POST) if form.is_valid(): user = form.cleaned_data['user'] ads_limit = form.cleaned_data['ads_limit'] if not user.region: from django.utils.safestring import mark_safe self.message_user( request, mark_safe( u'У пользователя отсутствует регион. <a href="%s">Указать регион</a>.' % reverse('admin:custom_user_user_change', args=[user.id])), level=messages.ERROR) return render(request, 'admin/buy_plan.html', locals()) try: plan = Plan.objects.get(is_active=True, ads_limit=ads_limit) discount = user.get_plan_discount() suggest_plan_price = Plan.get_price( user.region.price_level, ads_limit, discount) except Plan.DoesNotExist: plan = Plan.objects.get(pk=18) realtor = user.get_realtor() if realtor: agency_users = realtor.agency.get_realtors().exclude( user=user).values_list('user', flat=True) agency_ads_limits = (UserPlan.objects.filter(end__gt=datetime.datetime.now(), user__in=agency_users).aggregate(sum=Sum('ads_limit'))['sum'] or 0) \ + ads_limit else: agency_ads_limits = ads_limit if agency_ads_limits < 100: discount = 0.2 elif agency_ads_limits < 200: discount = 0.3 elif agency_ads_limits < 500: discount = 0.4 elif agency_ads_limits < 1000: discount = 0.45 else: discount = 0.5 suggest_plan_price = Plan.get_price( user.region.price_level, ads_limit, discount) if 'calculate' in request.POST: form.data._mutable = True form.data['price'] = suggest_plan_price self.message_user( request, u'%s лимит объявлений в агентстве, %s%% скидка. Цена за план %s грн. Баланс пользователя %s грн' % (ads_limit, int(discount * 100), suggest_plan_price, user.get_balance()), level=messages.WARNING) if 'buy' in request.POST: new_plan = UserPlan(user=user, plan=plan, ads_limit=ads_limit, region=user.region) active_plan = user.get_active_plan_using_prefetch() if active_plan: if form.cleaned_data['stop_active_plan']: active_plan.cancel(request.user) self.message_user( request, u'Предыдущий тариф #%s был отменен с возвратом средств' % active_plan.pk, level=messages.ERROR) else: new_plan.start = user.get_unexpired_plans( ).order_by('-end').first().end new_plan.is_active = False plan_price = form.cleaned_data[ 'price'] or suggest_plan_price if not (1 < (plan_price / ads_limit) < 30): self.message_user( request, u'Недопустимая цена за одно объявение: %.02f грн' % (plan_price / float(ads_limit)), level=messages.ERROR) elif user.get_balance(force=True) < plan_price: self.message_user( request, u'Недостаточно средств на счете. Тариф оценен в %s грн, баланс пользователя - %s' % (plan_price, user.get_balance()), level=messages.ERROR) else: # только здесь создается новый план new_plan.save() transaction = Transaction(user=user, type=11, amount=-plan_price, user_plan=new_plan) transaction.save() self.message_user( request, u'Тариф на %s объявлений c %s до %s куплен. Стоимость %s грн.' % (ads_limit, new_plan.start.strftime('%d.%m.%Y %H:%M'), new_plan.end.strftime('%d.%m.%Y %H:%M'), plan_price)) return redirect('.') else: form = Form() return render(request, 'admin/buy_plan.html', locals())
class VipPlacementAdmin(ReadOnlyModelAdmin): raw_id_fields = ('transaction', 'basead') list_display = ('id', 'user_link', 'property_link', 'days', 'since', 'until', 'transaction_link', 'is_active') list_filter = (make_datetime_filter('since'), make_datetime_filter('until')) actions = ('cancel',) def is_active(self, obj): return obj.is_active is_active.boolean = True def get_actions(self, request): actions = super(VipPlacementAdmin, self).get_actions(request) if not request.user.has_perm('paid_services.cancel_vipplacement'): del actions['cancel'] return actions def get_queryset(self, request): qs = super(VipPlacementAdmin, self).get_queryset(request) return qs.prefetch_related('basead', 'basead__ad__user', 'basead__ad__user__realtors__agency', 'transaction') def user_link(self, obj): if obj.basead_id: return get_user_filter_link(obj.basead.ad.user, lookup='basead__ad__user__exact') else: return u'нет объявления' user_link.allow_tags = True user_link.short_description = u"Пользователь" user_link.admin_order_field = 'ad__user' def transaction_link(self, obj): return u'<a href="%s">#%s</a>' % ( reverse('admin:paid_services_transaction_change', args=[obj.transaction_id]), obj.transaction_id) transaction_link.allow_tags = True transaction_link.short_description = u'транзакция' def lookup_allowed(self, key, value): if key in ('basead__ad__user', 'basead__ad__user__exact'): return True return super(VipPlacementAdmin, self).lookup_allowed(key, value) def property_link(self, obj): if obj.basead: return u'<a href="%s">#%s</a>' % (reverse('admin:ad_ad_change', args=[obj.basead_id]), obj.basead_id) else: return u'нет объявления' property_link.allow_tags = True property_link.short_description = u"Объявление" property_link.admin_order_field = 'ad' def cancel(self, request, queryset): cancelled = [] errors = [] for vip_placement in queryset.all(): if datetime.datetime.now() < vip_placement.until: vip_placement.cancel(request.user) cancelled.append('#%d' % vip_placement.id) else: errors.append('#%d' % vip_placement.id) message = u'Отменено %d платных размещений %s' % (len(cancelled), ', '.join(cancelled)) if errors: message += u', невозможно отменить %d платных размещений %s (уже закончились)' % ( len(errors), ', '.join(errors) ) self.message_user(request, message) cancel.short_description = u'Отмена платного размещения, возврат полной суммы на баланс'
class TransactionAdmin(admin.ModelAdmin): raw_id_fields = ('user', 'order', 'user_plan', 'corrected_transaction') list_display = ('id', 'time', 'user_link', 'amount', 'display_type', 'purchased', 'display_order', 'comment') list_display_links = ['id'] readonly_fields = ('user', 'type', 'amount', 'time') list_filter = (ManagerFilter, TypeFilter, RevertTypeFilter, make_datetime_filter('time'), UserFilter, PlanFilter, RegionFilter, MoneySumFilter) actions = ('cancel',) def cancel(self, request, queryset): for transaction in queryset: if transaction.type == 11: transaction.user_plan.cancel(request.user) elif transaction.type in [53,54,55]: for placement in list(obj.vipplacements.all()) + list(obj.catalogplacements.all()): placement.cancel(request.user) elif transaction.type in [84,85]: for call in transaction.paidcalls.all(): call.cancel(request.user) else: transaction.revert(comment=u'транзакция отменена %s' % request.user) cancel.short_description = u'Вернуть деньги' def get_readonly_fields(self, request, obj=None): if request.user.is_superuser: return [] else: return super(TransactionAdmin, self).get_readonly_fields(request, obj) def get_queryset(self, request): qs = super(TransactionAdmin, self).get_queryset(request) return qs.prefetch_related('vipplacements', 'catalogplacements', 'order', 'user_plan', 'user', 'user__realtors__agency') def user_link(self, obj): return get_user_filter_link(obj.user) user_link.allow_tags = True user_link.short_description = u'пользователь' def display_type(self, obj): if obj.type == 100 and obj.corrected_transaction: return u'%s (коррекция)' % (obj.corrected_transaction.get_type_display()) else: return obj.get_type_display() display_type.admin_order_field = 'type' display_type.short_description = u'тип' def purchased(self, obj): links = [] related = list(obj.vipplacements.all()) + list(obj.catalogplacements.all()) if obj.user_plan: related.append(obj.user_plan) for rel in related: url = reverse( 'admin:paid_services_%s_changelist' % rel._meta.model_name) + '?id=%s' % rel.id links.append(u'<a href="%s">%s #%s</a>' % (url, rel._meta.verbose_name, rel.id)) return u', '.join(links) purchased.allow_tags = True purchased.short_description = u'покупки' def display_order(self, obj): if obj.order: return '<a href="%s?id=%d">%d</a>' % (reverse('admin:paid_services_order_changelist'), obj.order_id, obj.order_id) else: return '' display_order.allow_tags = True display_order.short_description = u'заказ'