@ -0,0 +1,5 @@
|
||||
debug.log
|
||||
*/__pycache__
|
||||
*/*/__pycache__
|
||||
.idea
|
||||
*.pyc
|
@ -0,0 +1 @@
|
||||
#
|
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -0,0 +1,10 @@
|
||||
# myapp/apps.py
|
||||
from django.apps import AppConfig
|
||||
|
||||
class AppTestConfig(AppConfig):
|
||||
name = 'app_test'
|
||||
|
||||
def ready(self):
|
||||
# from .tasks import setup_periodic_tasks
|
||||
# setup_periodic_tasks()
|
||||
pass
|
@ -0,0 +1,148 @@
|
||||
import asyncio
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime, timedelta, date
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
import django
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
import requests
|
||||
from django.db import IntegrityError
|
||||
from lxml import etree
|
||||
from pylab import mpl
|
||||
|
||||
from .models import BeijingWeekData
|
||||
from .user_agents_pool import agent_list # 确保 user_agents_pool.py 文件在当前目录,并包含 agent_list
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'liugan_yuce.liugan_yuce.settings')
|
||||
django.setup()
|
||||
|
||||
mpl.rcParams["font.sans-serif"] = ["SimHei"]
|
||||
mpl.rcParams["axes.unicode_minus"] = False
|
||||
|
||||
class GetBeijingGanranShuju(object):
|
||||
def __init__(self):
|
||||
user_agent = random.choice(agent_list)
|
||||
self.headers = {
|
||||
"User-Agent": user_agent,
|
||||
}
|
||||
self.data = []
|
||||
self.link_list_2023 = []
|
||||
self.link_list_2024 = []
|
||||
|
||||
def get_Link_2023(self, url):
|
||||
response = requests.get(url=url, headers=self.headers)
|
||||
time.sleep(random.uniform(1, 3))
|
||||
html = response.content.decode("utf-8")
|
||||
link_2023 = re.findall('<a href="[.]*?(/.*?2023.*?)">', html)
|
||||
for i in link_2023:
|
||||
url_head = "https://www.bjcdc.org/"
|
||||
i = url_head + i
|
||||
self.link_list_2023.append(i)
|
||||
return self.link_list_2023
|
||||
|
||||
def get_Link_2024(self, url):
|
||||
response = requests.get(url=url, headers=self.headers)
|
||||
time.sleep(random.uniform(1, 3))
|
||||
html = response.content.decode("utf-8")
|
||||
link_2024 = re.findall('<a href="[.]*?(/.*?2024.*?)">', html)
|
||||
for i in link_2024:
|
||||
url_head = "https://www.bjcdc.org/"
|
||||
i = url_head + i
|
||||
self.link_list_2024.append(i)
|
||||
return self.link_list_2024
|
||||
|
||||
def get_content_2023(self, link):
|
||||
response = requests.get(url=link, headers=self.headers)
|
||||
import time
|
||||
time.sleep(random.uniform(1, 3))
|
||||
html = response.content.decode("utf-8")
|
||||
number_list = re.findall(r'(\d+)例', html, re.DOTALL)
|
||||
number = number_list[0] if number_list else ''
|
||||
time_list = re.findall(r'(\d+月\d+日至2023年\d+月\d+日)', html)
|
||||
if time_list:
|
||||
time_str = time_list[0]
|
||||
time1 = re.match(r'\d+月\d+日?', time_str).group()
|
||||
month_number = re.match(r'\d{1,2}', time1).group()
|
||||
day_number = re.findall(r'月(\d{1,2})', time1)[0]
|
||||
time = f'2023-{int(month_number):02d}-{int(day_number):02d}'
|
||||
if number.isdigit():
|
||||
self.data.append([time, number])
|
||||
|
||||
def get_content_2024(self, link):
|
||||
response = requests.get(url=link, headers=self.headers)
|
||||
import time
|
||||
time.sleep(random.uniform(1, 3))
|
||||
html = response.content.decode("utf-8")
|
||||
if '周' not in html:
|
||||
number_list = re.findall(r'(\d+)例', html, re.DOTALL)
|
||||
number = number_list[0] if number_list else ''
|
||||
time_list = re.findall(r'(\d+年\d+月)', html)
|
||||
if time_list:
|
||||
time = time_list[0]
|
||||
if number.isdigit():
|
||||
self.month_data.append([time, number])
|
||||
|
||||
def get_beijing_zhoubao():
|
||||
# 创建获取 获取北京传染病数据 类的实例
|
||||
get_beijing_ganran_shuju = GetBeijingGanranShuju()
|
||||
url_1 = ['https://www.bjcdc.org/cdcmodule/jkdt/yqbb/index.shtml']
|
||||
url_list2 = [f'https://www.bjcdc.org/cdcmodule/jkdt/yqbb/index_{i}.shtml' for i in range(2, 5)]
|
||||
url_list = url_1 + url_list2
|
||||
|
||||
# 2023
|
||||
for url in url_list:
|
||||
get_beijing_ganran_shuju.get_Link_2023(url)
|
||||
|
||||
# 使用多进程处理每个块
|
||||
pool = ThreadPool(100)
|
||||
pool.map(get_beijing_ganran_shuju.get_content_2023, reversed(get_beijing_ganran_shuju.link_list_2023))
|
||||
pool.close()
|
||||
pool.join()
|
||||
|
||||
# 2024
|
||||
get_beijing_ganran_shuju.month_data = []
|
||||
for url in url_list:
|
||||
get_beijing_ganran_shuju.get_Link_2024(url)
|
||||
for x in reversed(get_beijing_ganran_shuju.link_list_2024):
|
||||
get_beijing_ganran_shuju.get_content_2024(x)
|
||||
|
||||
df = pd.DataFrame(get_beijing_ganran_shuju.data, columns=['日期', '感染数量'])
|
||||
df = df[df['日期'] != '2023-12-26']
|
||||
df['日期'] = pd.to_datetime(df['日期'])
|
||||
df_week = df.sort_values(by='日期')
|
||||
from datetime import date
|
||||
today = date.today()
|
||||
start_date = datetime(2024, 1, 2)
|
||||
end_date = datetime.now()
|
||||
|
||||
dates = []
|
||||
while start_date <= end_date:
|
||||
dates.append(start_date)
|
||||
start_date += timedelta(days=7)
|
||||
|
||||
infections = {datetime.strptime(month, "%Y年%m月").strftime("%Y-%m"): int(int(total) / 4) for month, total in get_beijing_ganran_shuju.month_data}
|
||||
|
||||
date_infections = []
|
||||
for date in dates:
|
||||
month_key = date.strftime("%Y-%m")
|
||||
if month_key in infections:
|
||||
date_infections.append([date, infections[month_key]])
|
||||
|
||||
month_df = pd.DataFrame(date_infections, columns=['日期', '感染数量'])
|
||||
df = pd.concat([df_week, month_df])
|
||||
df = df.rename(columns={'日期': 'date', '感染数量': 'beijing_number'})
|
||||
print(df)
|
||||
|
||||
converted_data = df.values.tolist()
|
||||
for data in converted_data:
|
||||
obj, created = BeijingWeekData.objects.get_or_create(date=data[0], defaults={'infection_number': data[1]})
|
||||
if created:
|
||||
print(f"Added new record for date {data[0]} with infections {data[1]}")
|
||||
else:
|
||||
print(f"Record for date {data[0]} already exists.")
|
||||
print('成功载入数据库')
|
||||
|
@ -0,0 +1,2 @@
|
||||
from app_test.tasks import my_scheduled_task
|
||||
my_scheduled_task.delay()
|
@ -0,0 +1,39 @@
|
||||
from django import forms
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from .models import CaptchaModel, Fund
|
||||
|
||||
User = get_user_model()
|
||||
class FundForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Fund
|
||||
fields = ['fund_id', 'fund_name']
|
||||
class RegisterForm(forms.Form):
|
||||
username = forms.CharField(max_length=20,min_length=2,error_messages={
|
||||
'required':'请输入用户名',
|
||||
'max_length':'用户长度在2~20之间',
|
||||
'min_length':'用户长度在2~20之间'
|
||||
})
|
||||
email = forms.EmailField(error_messages={'required':'请输入邮箱','invalid':'请输入一个正确的邮箱!'})
|
||||
password = forms.CharField(max_length=20,min_length=6)
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data.get('email')
|
||||
exists = User.objects.filter(email=email).exists()
|
||||
if exists:
|
||||
raise forms.ValidationError('邮箱已经被注册')
|
||||
return email
|
||||
def clean_captcha(self):
|
||||
captcha = self.cleaned_data_get('captcha')
|
||||
email = self.cleaned_data_get('email')
|
||||
creat_time = self.cleaned_data_get('creat_time')
|
||||
captcha_model = Captcha.objects.filter(email=email,captcha=captcha).first()
|
||||
if not captcha_model:
|
||||
raise foroms.ValidationError('验证码错误')
|
||||
return captcha
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
email = forms.EmailField(error_messages={"required": '请传入邮箱!', 'invalid': '请传入一个正确的邮箱!'})
|
||||
password = forms.CharField(max_length=20, min_length=6)
|
||||
remember = forms.IntegerField(required=False)
|
||||
|
@ -0,0 +1,36 @@
|
||||
# # app_test/management/commands/test_celery_beat.py
|
||||
# from django.core.management.base import BaseCommand
|
||||
# from django_celery_beat.models import CrontabSchedule, PeriodicTask
|
||||
# from django.utils.timezone import now
|
||||
# import logging
|
||||
#
|
||||
# logger = logging.getLogger(__name__)
|
||||
#
|
||||
# class Command(BaseCommand):
|
||||
# help = 'Test Celery Beat and Worker'
|
||||
#
|
||||
# def handle(self, *args, **kwargs):
|
||||
# logger.info("Running test_celery_beat command")
|
||||
#
|
||||
# schedule, created = CrontabSchedule.objects.get_or_create(
|
||||
# minute='*/1', # 每分钟运行一次
|
||||
# hour='*',
|
||||
# day_of_week='*',
|
||||
# day_of_month='*',
|
||||
# month_of_year='*',
|
||||
# )
|
||||
#
|
||||
# # 删除已存在的任务
|
||||
# PeriodicTask.objects.filter(name='Test Task').delete()
|
||||
#
|
||||
# task, created = PeriodicTask.objects.get_or_create(
|
||||
# crontab=schedule,
|
||||
# name='Test Task',
|
||||
# task='app_test.tasks.write_to_file_task',
|
||||
# start_time=now(),
|
||||
# )
|
||||
# if created or not task.enabled:
|
||||
# task.enabled = True
|
||||
# task.save()
|
||||
#
|
||||
# self.stdout.write(self.style.SUCCESS('Successfully set up test periodic tasks'))
|
@ -0,0 +1,33 @@
|
||||
# # app_test/management/commands/test_celery_beat.py
|
||||
# from django.core.management.base import BaseCommand
|
||||
# from django_celery_beat.models import CrontabSchedule, PeriodicTask
|
||||
# from django.utils.timezone import now
|
||||
# import logging
|
||||
#
|
||||
# logger = logging.getLogger(__name__)
|
||||
#
|
||||
# class Command(BaseCommand):
|
||||
# help = 'Test Celery Beat and Worker'
|
||||
#
|
||||
# def handle(self, *args, **kwargs):
|
||||
# logger.info("Running test_celery_beat command")
|
||||
#
|
||||
# schedule, created = CrontabSchedule.objects.get_or_create(
|
||||
# minute='*/1', # 每分钟运行一次
|
||||
# hour='*',
|
||||
# day_of_week='*',
|
||||
# day_of_month='*',
|
||||
# month_of_year='*',
|
||||
# )
|
||||
#
|
||||
# task, created = PeriodicTask.objects.get_or_create(
|
||||
# crontab=schedule,
|
||||
# name='Test Task',
|
||||
# task='app_test.tasks.test_task',
|
||||
# start_time=now(),
|
||||
# )
|
||||
# if created or not task.enabled:
|
||||
# task.enabled = True
|
||||
# task.save()
|
||||
#
|
||||
# self.stdout.write(self.style.SUCCESS('Successfully set up test periodic tasks'))
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.0.6 on 2024-05-24 08:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CaptchaModel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('captcha', models.CharField(max_length=4)),
|
||||
('create_time', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.0.6 on 2024-05-24 09:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app_test', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='captchamodel',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, unique=True),
|
||||
),
|
||||
]
|
@ -0,0 +1,69 @@
|
||||
# Generated by Django 5.0.6 on 2024-05-25 10:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app_test', '0002_alter_captchamodel_email'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BaiduData',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('liugan_index', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BeijingWeekData',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('infection_number', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GFData',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('gf_data', models.FloatField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HXData',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('hx_data', models.FloatField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='JijinData',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('jijin_data', models.FloatField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LiuganWeekData',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('infection_number', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StockData',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('shoupan', models.FloatField()),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,25 @@
|
||||
# Generated by Django 5.0.6 on 2024-05-25 15:58
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app_test', '0003_baidudata_beijingweekdata_gfdata_hxdata_jijindata_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Fund',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('fund_id', models.IntegerField()),
|
||||
('fund_name', models.CharField()),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,31 @@
|
||||
# Generated by Django 5.0.6 on 2024-05-27 10:38
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app_test', '0004_fund'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='fund',
|
||||
name='fund_id',
|
||||
field=models.CharField(max_length=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='fund',
|
||||
name='fund_name',
|
||||
field=models.CharField(max_length=20),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='fund',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='funds', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-18 12:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app_test', '0005_alter_fund_fund_id_alter_fund_fund_name_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='RecommendedFund',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('fund_code', models.CharField(max_length=6)),
|
||||
('fund_name', models.CharField(max_length=20)),
|
||||
('data_js', models.JSONField(default=None)),
|
||||
('date_js', models.JSONField(default=None)),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,62 @@
|
||||
rom django.db import models
|
||||
#导入内置的model User才能使用外键连接
|
||||
from django.contrib.auth.models import User
|
||||
# 导入内置的model User才能使用外键连接
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class LiuganWeekData(models.Model):
|
||||
date = models.DateField()
|
||||
infection_number = models.IntegerField()
|
||||
class Meta:
|
||||
app_label = 'app_test' # 确保这与你的应用名称匹配
|
||||
class BeijingWeekData(models.Model):
|
||||
date = models.DateField()
|
||||
infection_number = models.IntegerField()
|
||||
|
||||
class HXData(models.Model):
|
||||
date = models.DateField()
|
||||
hx_data = models.FloatField()
|
||||
|
||||
class GFData(models.Model):
|
||||
date = models.DateField()
|
||||
gf_data = models.FloatField()
|
||||
|
||||
class JijinData(models.Model):
|
||||
date = models.DateField()
|
||||
jijin_data = models.FloatField()
|
||||
|
||||
|
||||
class BaiduData(models.Model):
|
||||
date = models.DateField()
|
||||
liugan_index = models.IntegerField()
|
||||
|
||||
class StockData(models.Model):
|
||||
date = models.DateField()
|
||||
shoupan = models.FloatField()
|
||||
|
||||
# 使用外键,储存用户的基金数据
|
||||
class Fund(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='funds')
|
||||
fund_id = models.CharField(max_length=6) # 指定 max_length
|
||||
fund_name = models.CharField(max_length=20) # 指定 max_length
|
||||
|
||||
def __str__(self):
|
||||
return self.fund_name
|
||||
|
||||
class CaptchaModel(models.Model):
|
||||
email = models.EmailField(unique=True)
|
||||
captcha = models.CharField(max_length=4)
|
||||
create_time = models.DateTimeField(auto_now_add=True)
|
||||
# class Meta:
|
||||
# app_label = 'app_test'
|
||||
|
||||
#推荐的流感基金
|
||||
class RecommendedFund(models.Model):
|
||||
fund_code = models.CharField(max_length=6)
|
||||
fund_name = models.CharField(max_length=20)
|
||||
data_js = models.JSONField(default=None)
|
||||
date_js = models.JSONField(default=None)
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "app_test",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/bootstrap-icons": {
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmmirror.com/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
|
||||
"integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019-2024 The Bootstrap Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,100 @@
|
||||
<p align="center">
|
||||
<a href="https://getbootstrap.com/">
|
||||
<img src="https://getbootstrap.com/docs/5.2/assets/brand/bootstrap-logo-shadow.png" alt="Bootstrap logo" width="200" height="165">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h3 align="center">Bootstrap Icons</h3>
|
||||
|
||||
<p align="center">
|
||||
Official open source SVG icon library for Bootstrap with over 2,000 icons.
|
||||
<br>
|
||||
<a href="https://icons.getbootstrap.com/"><strong>Explore Bootstrap Icons »</strong></a>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://getbootstrap.com/">Bootstrap</a>
|
||||
·
|
||||
<a href="https://themes.getbootstrap.com/">Themes</a>
|
||||
·
|
||||
<a href="https://blog.getbootstrap.com/">Blog</a>
|
||||
<br>
|
||||
</p>
|
||||
|
||||
[![Bootstrap Icons preview](https://github.com/twbs/icons/blob/main/.github/preview.png)](https://icons.getbootstrap.com/)
|
||||
|
||||
## Install
|
||||
|
||||
Bootstrap Icons are packaged up and published to npm. We only include the processed SVGs in this package—it's up to you and your team to implement. [Read our docs](https://icons.getbootstrap.com/) for usage instructions.
|
||||
|
||||
```shell
|
||||
npm i bootstrap-icons
|
||||
```
|
||||
|
||||
For those [using Packagist](https://packagist.org/packages/twbs/bootstrap-icons), you can also install Bootstrap Icons via Composer:
|
||||
|
||||
```shell
|
||||
composer require twbs/bootstrap-icons
|
||||
```
|
||||
|
||||
[Also available in Figma](https://www.figma.com/community/file/1042482994486402696/Bootstrap-Icons).
|
||||
|
||||
## Usage
|
||||
|
||||
Depending on your setup, you can include Bootstrap Icons in a handful of ways.
|
||||
|
||||
- Copy-paste SVGs as embedded HTML
|
||||
- Reference via `<img>` element
|
||||
- Use the SVG sprite
|
||||
- Include via CSS
|
||||
|
||||
[See the docs for more information](https://icons.getbootstrap.com/#usage).
|
||||
|
||||
## Development
|
||||
|
||||
[![Build Status](https://img.shields.io/github/actions/workflow/status/twbs/icons/test.yml?branch=main&label=Tests&logo=github)](https://github.com/twbs/icons/actions/workflows/test.yml?query=workflow%3ATests+branch%3Amain)
|
||||
[![npm version](https://img.shields.io/npm/v/bootstrap-icons?logo=npm&logoColor=fff)](https://www.npmjs.com/package/bootstrap-icons)
|
||||
|
||||
Clone the repo, install dependencies, and start the Hugo server locally.
|
||||
|
||||
```shell
|
||||
git clone https://github.com/twbs/icons/
|
||||
cd icons
|
||||
npm i
|
||||
npm start
|
||||
```
|
||||
|
||||
Then open `http://localhost:4000` in your browser.
|
||||
|
||||
### npm scripts
|
||||
|
||||
Here are some key scripts you'll use during development. Be sure to look to our `package.json` or `npm run` output for a complete list of scripts.
|
||||
|
||||
| Script | Description |
|
||||
|--------------|-------------------------------------------------------------------------------|
|
||||
| `start` | Alias for running `docs-serve` |
|
||||
| `docs-serve` | Starts a local Hugo server |
|
||||
| `pages` | Generates permalink pages for each icon with template Markdown |
|
||||
| `icons` | Processes and optimizes SVGs in `icons` directory, generates fonts and sprite |
|
||||
|
||||
## Adding SVGs
|
||||
|
||||
Icons are typically only added by @mdo, but exceptions can be made. New glyphs are designed in Figma first on a 16x16px grid, then exported as flattened SVGs with `fill` (no stroke). Once a new SVG icon has been added to the `icons` directory, we use an npm script to:
|
||||
|
||||
1. Optimize our SVGs with SVGO.
|
||||
2. Modify the SVGs source code, removing all attributes before setting new attributes and values in our preferred order.
|
||||
|
||||
Use `npm run icons` to run the script, run `npm run pages` to build permalink pages, complete those pages, and, finally, commit the results in a new branch for updating.
|
||||
|
||||
**Warning**: Please exclude any auto-generated files, like `font/**` and `bootstrap-icons.svg` from your branch because they cause conflicts, and we generally update the dist files before a release.
|
||||
|
||||
## Publishing
|
||||
|
||||
Documentation is published automatically when a new Git tag is published. See our [GitHub Actions](https://github.com/twbs/icons/tree/main/.github/workflows) and [`package.json`](https://github.com/twbs/icons/blob/main/package.json) for more information.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
## Author
|
||||
|
||||
[@mdo](https://github.com/mdo)
|
After Width: | Height: | Size: 1.0 MiB |
After Width: | Height: | Size: 476 B |
After Width: | Height: | Size: 507 B |
After Width: | Height: | Size: 514 B |
After Width: | Height: | Size: 579 B |
After Width: | Height: | Size: 250 B |
After Width: | Height: | Size: 279 B |
After Width: | Height: | Size: 286 B |
After Width: | Height: | Size: 366 B |
After Width: | Height: | Size: 854 B |
After Width: | Height: | Size: 457 B |
After Width: | Height: | Size: 477 B |
After Width: | Height: | Size: 484 B |
After Width: | Height: | Size: 564 B |
After Width: | Height: | Size: 607 B |
After Width: | Height: | Size: 642 B |
After Width: | Height: | Size: 634 B |
After Width: | Height: | Size: 714 B |
After Width: | Height: | Size: 359 B |
After Width: | Height: | Size: 421 B |
After Width: | Height: | Size: 428 B |
After Width: | Height: | Size: 493 B |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 514 B |
After Width: | Height: | Size: 521 B |
After Width: | Height: | Size: 601 B |
After Width: | Height: | Size: 617 B |
After Width: | Height: | Size: 640 B |
After Width: | Height: | Size: 662 B |
After Width: | Height: | Size: 727 B |
After Width: | Height: | Size: 251 B |
After Width: | Height: | Size: 279 B |
After Width: | Height: | Size: 286 B |
After Width: | Height: | Size: 366 B |
After Width: | Height: | Size: 686 B |
After Width: | Height: | Size: 717 B |
After Width: | Height: | Size: 737 B |
After Width: | Height: | Size: 804 B |
After Width: | Height: | Size: 574 B |
After Width: | Height: | Size: 597 B |
After Width: | Height: | Size: 620 B |
After Width: | Height: | Size: 684 B |
After Width: | Height: | Size: 366 B |
After Width: | Height: | Size: 687 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 492 B |
After Width: | Height: | Size: 840 B |
After Width: | Height: | Size: 615 B |
After Width: | Height: | Size: 689 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 264 B |
After Width: | Height: | Size: 311 B |
After Width: | Height: | Size: 315 B |
After Width: | Height: | Size: 311 B |
After Width: | Height: | Size: 315 B |
After Width: | Height: | Size: 281 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 845 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 324 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 230 B |
After Width: | Height: | Size: 432 B |
After Width: | Height: | Size: 1.4 KiB |